zurb-foundation 3.2.5 → 4.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (239) hide show
  1. data/.gitignore +6 -2
  2. data/CHANGELOG.md +1 -26
  3. data/CONTRIBUTING.md +18 -0
  4. data/Gemfile.lock +19 -0
  5. data/Gruntfile.js +27 -0
  6. data/README.md +5 -6
  7. data/{Capfile → docs/Capfile} +0 -1
  8. data/docs/Gemfile +8 -0
  9. data/docs/Gemfile.lock +43 -0
  10. data/docs/Procfile +2 -0
  11. data/docs/README.md +1 -0
  12. data/docs/_sidebar-components.html.erb +109 -0
  13. data/docs/_sidebar.html.erb +109 -0
  14. data/docs/_zurb-jobs.html.erb +5 -0
  15. data/docs/changelog.html.erb +185 -0
  16. data/docs/compile.rb +43 -0
  17. data/docs/components/alert-boxes.html.erb +202 -0
  18. data/docs/components/block-grid.html.erb +118 -0
  19. data/docs/components/breadcrumbs.html.erb +146 -0
  20. data/docs/components/button-groups.html.erb +174 -0
  21. data/docs/components/buttons.html.erb +220 -0
  22. data/docs/components/clearing.html.erb +152 -0
  23. data/docs/components/custom-forms.html.erb +306 -0
  24. data/docs/components/dropdown-buttons.html.erb +233 -0
  25. data/docs/components/dropdown.html.erb +186 -0
  26. data/docs/components/flex-video.html.erb +93 -0
  27. data/docs/components/forms.html.erb +468 -0
  28. data/docs/components/grid.html.erb +355 -0
  29. data/docs/components/inline-lists.html.erb +89 -0
  30. data/docs/components/joyride.html.erb +178 -0
  31. data/docs/components/keystrokes.html.erb +74 -0
  32. data/docs/components/labels.html.erb +98 -0
  33. data/docs/components/magellan.html.erb +84 -0
  34. data/docs/components/orbit.html.erb +262 -0
  35. data/docs/components/pagination.html.erb +181 -0
  36. data/docs/components/panels.html.erb +121 -0
  37. data/docs/components/pricing-tables.html.erb +154 -0
  38. data/docs/components/progress-bars.html.erb +120 -0
  39. data/docs/components/reveal.html.erb +147 -0
  40. data/docs/components/section.html.erb +156 -0
  41. data/docs/components/side-nav.html.erb +122 -0
  42. data/docs/components/split-buttons.html.erb +218 -0
  43. data/docs/components/sub-nav.html.erb +120 -0
  44. data/docs/components/switch.html.erb +288 -0
  45. data/docs/components/tables.html.erb +123 -0
  46. data/docs/components/thumbnails.html.erb +87 -0
  47. data/docs/components/tooltips.html.erb +73 -0
  48. data/docs/components/top-bar.html.erb +219 -0
  49. data/docs/components/type.html.erb +359 -0
  50. data/docs/components/visibility.html.erb +102 -0
  51. data/docs/config.ru +12 -0
  52. data/docs/config/deploy.rb +33 -0
  53. data/docs/controller.rb +43 -0
  54. data/docs/css/_coderay.scss +116 -0
  55. data/docs/css/_settings.scss +1 -0
  56. data/docs/css/docs.scss +174 -0
  57. data/docs/css/normalize.scss +396 -0
  58. data/docs/css/qunit-composite.css +13 -0
  59. data/docs/css/qunit.css +235 -0
  60. data/docs/faq.html.erb +61 -0
  61. data/docs/img/demos/demo1-th.png +0 -0
  62. data/docs/img/demos/demo1.png +0 -0
  63. data/docs/img/demos/demo2-th.png +0 -0
  64. data/docs/img/demos/demo2.png +0 -0
  65. data/docs/img/demos/demo3-th.png +0 -0
  66. data/docs/img/demos/demo3.png +0 -0
  67. data/docs/img/demos/demo4-th.png +0 -0
  68. data/docs/img/demos/demo4.png +0 -0
  69. data/docs/img/demos/demo5-th.png +0 -0
  70. data/docs/img/demos/demo5.png +0 -0
  71. data/docs/index.html.erb +299 -0
  72. data/docs/javascript.html.erb +133 -0
  73. data/docs/js/docs.js +3 -0
  74. data/docs/js/qunit-composite.js +105 -0
  75. data/docs/js/qunit.js +1977 -0
  76. data/docs/js/tests/tabs/simple_tabs.html +57 -0
  77. data/docs/js/tests/tabs/simple_tabs.js +54 -0
  78. data/docs/js/tests/tooltips/tooltips.html +39 -0
  79. data/docs/js/tests/tooltips/tooltips.js +11 -0
  80. data/docs/layout.html.erb +99 -0
  81. data/docs/rails.html.erb +66 -0
  82. data/docs/sass.html.erb +299 -0
  83. data/docs/support.html.erb +134 -0
  84. data/foundation.gemspec +2 -4
  85. data/index.html +3 -23
  86. data/js/foundation/foundation.alerts.js +50 -0
  87. data/js/foundation/foundation.clearing.js +478 -0
  88. data/{vendor/assets/javascripts/foundation/jquery.cookie.js → js/foundation/foundation.cookie.js} +3 -1
  89. data/js/foundation/foundation.dropdown.js +122 -0
  90. data/js/foundation/foundation.forms.js +403 -0
  91. data/js/foundation/foundation.joyride.js +613 -0
  92. data/js/foundation/foundation.js +331 -0
  93. data/js/foundation/foundation.magellan.js +130 -0
  94. data/js/foundation/foundation.orbit.js +355 -0
  95. data/{vendor/assets/javascripts/foundation/jquery.placeholder.js → js/foundation/foundation.placeholder.js} +4 -2
  96. data/js/foundation/foundation.reveal.js +264 -0
  97. data/js/foundation/foundation.section.js +180 -0
  98. data/js/foundation/foundation.tooltips.js +195 -0
  99. data/js/foundation/foundation.topbar.js +187 -0
  100. data/js/foundation/index.js +16 -0
  101. data/{vendor/assets/javascripts/foundation/modernizr.foundation.js → js/vendor/custom.modernizr.js} +0 -0
  102. data/js/vendor/jquery.js +9597 -0
  103. data/js/vendor/zepto.js +1782 -0
  104. data/lib/foundation/engine.rb +8 -1
  105. data/lib/foundation/generators/install_generator.rb +24 -1
  106. data/lib/foundation/generators/templates/application.html.erb +20 -6
  107. data/lib/foundation/generators/templates/application.html.haml +2 -6
  108. data/lib/foundation/generators/templates/application.html.slim +2 -5
  109. data/lib/foundation/version.rb +1 -1
  110. data/lib/zurb-foundation.rb +7 -9
  111. data/package.json +9 -0
  112. data/scss/foundation.scss +42 -15
  113. data/scss/foundation/_foundation-global.scss +226 -0
  114. data/scss/foundation/components/_alert-boxes.scss +106 -0
  115. data/scss/foundation/components/_block-grid.scss +63 -0
  116. data/scss/foundation/components/_breadcrumbs.scss +117 -0
  117. data/scss/foundation/components/_button-groups.scss +59 -0
  118. data/scss/foundation/components/_buttons.scss +217 -0
  119. data/scss/foundation/components/_clearing.scss +209 -0
  120. data/scss/foundation/components/_custom-forms.scss +232 -0
  121. data/scss/foundation/components/_dropdown-buttons.scss +114 -0
  122. data/scss/foundation/components/_dropdown.scss +137 -0
  123. data/scss/foundation/components/_flex-video.scss +45 -0
  124. data/scss/foundation/components/_forms.scss +309 -0
  125. data/scss/foundation/components/_grid.scss +149 -71
  126. data/scss/foundation/components/_inline-lists.scss +47 -0
  127. data/scss/foundation/components/_joyride.scss +193 -0
  128. data/scss/foundation/components/_keystrokes.scss +56 -0
  129. data/scss/foundation/components/_labels.scss +81 -0
  130. data/scss/foundation/components/_magellan.scss +15 -0
  131. data/scss/foundation/components/_orbit.scss +193 -0
  132. data/scss/foundation/components/_pagination.scss +99 -0
  133. data/scss/foundation/components/_panels.scss +76 -0
  134. data/scss/foundation/components/_pricing-tables.scss +130 -0
  135. data/scss/foundation/components/_progress-bars.scss +68 -0
  136. data/scss/foundation/components/_reveal.scss +131 -0
  137. data/scss/foundation/components/_section.scss +194 -0
  138. data/scss/foundation/components/_side-nav.scss +68 -0
  139. data/scss/foundation/components/_split-buttons.scss +159 -0
  140. data/scss/foundation/components/_sub-nav.scss +67 -0
  141. data/scss/foundation/components/_switch.scss +242 -0
  142. data/scss/foundation/components/_tables.scss +80 -0
  143. data/scss/foundation/components/_thumbs.scss +45 -0
  144. data/scss/foundation/components/_tooltips.scss +113 -0
  145. data/scss/foundation/components/_top-bar.scss +443 -0
  146. data/scss/foundation/components/_type.scss +411 -0
  147. data/scss/foundation/components/_visibility.scss +117 -0
  148. data/scss/normalize.scss +396 -0
  149. data/templates/project/config.rb +26 -0
  150. data/templates/project/index.html +28 -43
  151. data/templates/project/manifest.rb +21 -28
  152. data/templates/project/scss/_settings.scss +4 -243
  153. data/templates/project/scss/app.scss +37 -44
  154. data/templates/project/scss/normalize.scss +396 -0
  155. data/test/stylesheets/styles.css +955 -0
  156. metadata +144 -153
  157. data/config/deploy.rb +0 -42
  158. data/lib/foundation/generators/layout_generator.rb +0 -28
  159. data/scss/foundation/_settings.scss +0 -281
  160. data/scss/foundation/common/_base.scss +0 -4
  161. data/scss/foundation/common/_forms.scss +0 -117
  162. data/scss/foundation/common/_globals.scss +0 -35
  163. data/scss/foundation/common/_ratios.scss +0 -19
  164. data/scss/foundation/common/_typography.scss +0 -104
  165. data/scss/foundation/components/modules/_all.scss +0 -10
  166. data/scss/foundation/components/modules/_buttons.scss +0 -178
  167. data/scss/foundation/components/modules/_clearing.scss +0 -61
  168. data/scss/foundation/components/modules/_joyride.scss +0 -33
  169. data/scss/foundation/components/modules/_mqueries.scss +0 -458
  170. data/scss/foundation/components/modules/_navbar.scss +0 -74
  171. data/scss/foundation/components/modules/_offcanvas.scss +0 -55
  172. data/scss/foundation/components/modules/_orbit.scss +0 -90
  173. data/scss/foundation/components/modules/_reveal.scss +0 -34
  174. data/scss/foundation/components/modules/_tabs.scss +0 -67
  175. data/scss/foundation/components/modules/_topbar.scss +0 -167
  176. data/scss/foundation/components/modules/_ui.scss +0 -292
  177. data/scss/foundation/functions/_all.scss +0 -2
  178. data/scss/foundation/functions/_convert-number-to-word.scss +0 -10
  179. data/scss/foundation/functions/_grid-calc.scss +0 -5
  180. data/scss/foundation/functions/modular-scale.scss +0 -3
  181. data/scss/foundation/mixins/_all.scss +0 -5
  182. data/scss/foundation/mixins/_clearfix.scss +0 -13
  183. data/scss/foundation/mixins/_css-triangle.scss +0 -22
  184. data/scss/foundation/mixins/_font-size.scss +0 -13
  185. data/scss/foundation/mixins/_respond-to.scss +0 -11
  186. data/scss/foundation/mixins/_semantic-grid.scss +0 -66
  187. data/test/buttons.html +0 -189
  188. data/test/clearing.html +0 -85
  189. data/test/config.rb +0 -11
  190. data/test/elements.html +0 -490
  191. data/test/forms.html +0 -371
  192. data/test/grid.html +0 -543
  193. data/test/images/orbit-demo/demo1.jpeg +0 -0
  194. data/test/images/orbit-demo/demo2.jpeg +0 -0
  195. data/test/images/orbit-demo/demo3.jpeg +0 -0
  196. data/test/images/orbit-demo/slider-background.jpeg +0 -0
  197. data/test/index.html +0 -83
  198. data/test/joyride.html +0 -127
  199. data/test/magellan.html +0 -112
  200. data/test/navigation.html +0 -269
  201. data/test/orbit.html +0 -112
  202. data/test/reveal.html +0 -91
  203. data/test/scss/_settings.scss +0 -245
  204. data/test/scss/styles.scss +0 -50
  205. data/test/tabs.html +0 -197
  206. data/test/template.html +0 -52
  207. data/test/topbar-login.html +0 -194
  208. data/test/topbar.html +0 -139
  209. data/test/type.html +0 -188
  210. data/vendor/assets/images/foundation/orbit/bullets.jpg +0 -0
  211. data/vendor/assets/images/foundation/orbit/left-arrow-small.png +0 -0
  212. data/vendor/assets/images/foundation/orbit/left-arrow.png +0 -0
  213. data/vendor/assets/images/foundation/orbit/loading.gif +0 -0
  214. data/vendor/assets/images/foundation/orbit/mask-black.png +0 -0
  215. data/vendor/assets/images/foundation/orbit/pause-black.png +0 -0
  216. data/vendor/assets/images/foundation/orbit/right-arrow-small.png +0 -0
  217. data/vendor/assets/images/foundation/orbit/right-arrow.png +0 -0
  218. data/vendor/assets/images/foundation/orbit/rotator-black.png +0 -0
  219. data/vendor/assets/images/foundation/orbit/timer-black.png +0 -0
  220. data/vendor/assets/javascripts/foundation/app.js +0 -41
  221. data/vendor/assets/javascripts/foundation/index.js +0 -19
  222. data/vendor/assets/javascripts/foundation/jquery.event.move.js +0 -580
  223. data/vendor/assets/javascripts/foundation/jquery.event.swipe.js +0 -130
  224. data/vendor/assets/javascripts/foundation/jquery.foundation.accordion.js +0 -47
  225. data/vendor/assets/javascripts/foundation/jquery.foundation.alerts.js +0 -20
  226. data/vendor/assets/javascripts/foundation/jquery.foundation.buttons.js +0 -83
  227. data/vendor/assets/javascripts/foundation/jquery.foundation.clearing.js +0 -413
  228. data/vendor/assets/javascripts/foundation/jquery.foundation.forms.js +0 -502
  229. data/vendor/assets/javascripts/foundation/jquery.foundation.joyride.js +0 -639
  230. data/vendor/assets/javascripts/foundation/jquery.foundation.magellan.js +0 -96
  231. data/vendor/assets/javascripts/foundation/jquery.foundation.mediaQueryToggle.js +0 -27
  232. data/vendor/assets/javascripts/foundation/jquery.foundation.navigation.js +0 -55
  233. data/vendor/assets/javascripts/foundation/jquery.foundation.orbit.js +0 -919
  234. data/vendor/assets/javascripts/foundation/jquery.foundation.reveal.js +0 -794
  235. data/vendor/assets/javascripts/foundation/jquery.foundation.tabs.js +0 -66
  236. data/vendor/assets/javascripts/foundation/jquery.foundation.tooltips.js +0 -211
  237. data/vendor/assets/javascripts/foundation/jquery.foundation.topbar.js +0 -174
  238. data/vendor/assets/javascripts/foundation/jquery.js +0 -9555
  239. data/vendor/assets/javascripts/foundation/jquery.offcanvas.js +0 -50
@@ -0,0 +1,133 @@
1
+ <div class="row">
2
+ <div class="large-9 push-3 columns">
3
+ <% @page_title = "Javascript" %>
4
+ <div class="row">
5
+ <div class="large-12 columns">
6
+ <h2><%= @page_title %></h2>
7
+ <h4 class="subheader">Foundation 4 streamlines the implementation of the Foundations plugins by combining them all into a single plugin under the <code>$.fn.foundation()</code> Zepto/jQuery namespace.</h4>
8
+ <hr>
9
+
10
+ <h2>Installation</h2>
11
+ <p>Foundation JavaScript was designed to work with <a href="http://zeptojs.com/" title="Zepto Docs">Zepto</a> and <a href="http://jquery.com/" title="jQuery Docs">jQuery</a> right out of the gate. Zepto is not supported by all browsers, so as suggested in the Zepto documentation, you should test for compatability and load Zepto or jQuery as necessary.</p>
12
+
13
+ <h5>Include Libraries</h5>
14
+ <p>In the <code>head</code> section of your page add Modernizr. Modernizr acts as a shim for HTML5 elements for older browsers as well as detection for mobile devices.</p>
15
+
16
+ <%= code_example '
17
+ <script src="/js/custom.modernizr.js></script>
18
+ ', :html %>
19
+
20
+ <p>Before the closing <code>body</code> tag, insert the following JavaScript.</p>
21
+
22
+ <%= code_example "
23
+ <!-- Check for Zepto support, load jQuery if necessary -->
24
+ <script>
25
+ document.write('<script src=/js/vendor/'
26
+ + ('__proto__' in {} ? 'zepto' : 'jquery')
27
+ + '.js><\/script>');
28
+ </script>
29
+ ", :html %>
30
+
31
+ <p>Then include either <code>foundation.min.js</code>, which includes Foundation Core and all JavaScript plugins or you can load them invidually.</p>
32
+
33
+ <%= code_example '
34
+ <script src="/js/foundation.min.js"></script>
35
+
36
+ <!-- or individualy -->
37
+
38
+ <script src="/js/foundation.js"></script>
39
+ <script src="/js/foundation.alerts.js"></script>
40
+ <!-- ... -->
41
+ <script src="/js/foundation.dropdown.js"></script>
42
+ <script src="/js/foundation.section.js"></script>
43
+ ', :html %>
44
+
45
+ <h5>Initialize Foundation</h5>
46
+ <p>After you have included the Foundation JavaScript, just add a simple call to initialize all plugins on your page.</p>
47
+
48
+ <%= code_example '
49
+ <script>
50
+ $(document).foundation();
51
+ </script>
52
+ ', :html %>
53
+
54
+ <p><strong>Note:</strong> We include tested versions of jQuery and Zepto in the Foundation repo to get you started quickly. If you want to build your own version of Zepto, Foundation employs the <i>event</i>, <i>fx</i>, <i>fx_methods</i>, <i>ajax</i>, <i>form</i>, <i>assets</i>, <i>data</i>, <i>selector</i>, and <i>stack</i> modules.</p>
55
+
56
+ <hr>
57
+
58
+ <h2>Configuration</h2>
59
+
60
+ <p>Foundation accepts a myriad of options for initialization. By default, calling <code>$('#scope').foundation();</code> will intialize all available plugins on the page. Alternatively, you can pass individual plugins along with configuration options and a callback. This will allow you to select specific plugins to start on your page, even if their source is included. You can take a look at the call here:</p>
61
+
62
+ <%= code_example "
63
+ $(document).foundation('section', {deep_linking: true}, function (response) {
64
+ console.log(response.errors);
65
+ });
66
+ ", :js %>
67
+
68
+ <p>The call will initialize the Section plugin on the page with deep linking enabled and pass back a response object with any errors that may have occured.</p>
69
+
70
+ <p>You can initialize multiple libraries within the same call as well. Here are a few ways this can be done:</p>
71
+
72
+ <%= code_example "
73
+ $(document).foundation('section dropdown alerts', {callback: myCallbackFunction}, function (response) {
74
+ console.log(response.errors);
75
+ });
76
+
77
+ // alternative, no configuration
78
+
79
+ $(document).foundation('section dropdown alerts', function (response) {
80
+ console.log(response.errors);
81
+ });
82
+
83
+ // alternative, all plugins, only callback
84
+
85
+ $(document).foundation(function (response) {
86
+ console.log(response.errors);
87
+ });
88
+
89
+ // alternative, only plugin, no config or callback
90
+
91
+ $(document).foundation('section');
92
+ ", :js %>
93
+
94
+ <p>Keep in mind, if you initialize multiple plugins at one time and pass a configuration object that object will get passed to all plugins that were initialized. So in the first example: section, dropdown, and alerts will all get passed the <code>myCallbackFunction</code> on initialization.</p>
95
+
96
+ <p>What if you wanted to pass the <code>myCallbackFunction</code> only to Section and still initialize the other plugins as well? You could do the following.</p>
97
+
98
+ <%= code_example "
99
+ $(document)
100
+ .foundation('dropdown alerts')
101
+ .foundation('section', {callback: myCallbackFunction});
102
+ ", :js %>
103
+
104
+ <hr>
105
+
106
+ <h2>Calling Internal Methods</h2>
107
+
108
+ <p>Foundation 4 JavaScript allows you call call internal plugin methods by passing the method name as the second argument. This is necessary for plugins like Joyride, since they are not intialized on page load by default.</p>
109
+
110
+ <p>This will fire the <code>start</code> method on Joyride:</p>
111
+
112
+ <%= code_example "
113
+ $(document).foundation('joyride', 'start');
114
+ ", :js %>
115
+
116
+ <h5>Unbind Events</h5>
117
+
118
+ <p>All plugins can be unbound from the DOM by firing the <code>off</code> event either on that plugin or globally to unbind all Foundation events.</p>
119
+
120
+ <%= code_example "
121
+ $(document).foundation('tooltips', 'off');
122
+
123
+ // or globally
124
+
125
+ $(document).foundation('off');
126
+ ", :js %>
127
+ </div>
128
+ </div>
129
+ </div>
130
+ <div class="large-3 pull-9 columns">
131
+ <%= render "_sidebar.html.erb" %>
132
+ </div>
133
+ </div>
@@ -0,0 +1,3 @@
1
+ /*
2
+ =require foundation
3
+ */
@@ -0,0 +1,105 @@
1
+ (function( QUnit ) {
2
+
3
+ QUnit.extend( QUnit, {
4
+ testSuites: function( suites ) {
5
+ QUnit.begin(function() {
6
+ QUnit.initIframe();
7
+ });
8
+
9
+ for ( var i = 0; i < suites.length; i++ ) {
10
+ QUnit.runSuite( suites[i] );
11
+ }
12
+
13
+ QUnit.done(function() {
14
+ this.iframe.style.display = "none";
15
+ });
16
+ },
17
+
18
+ runSuite: function( suite ) {
19
+ asyncTest( suite, function() {
20
+ QUnit.iframe.setAttribute( "src", suite );
21
+ });
22
+ },
23
+
24
+ initIframe: function() {
25
+ var body = document.body,
26
+ iframe = this.iframe = document.createElement( "iframe" ),
27
+ iframeWin;
28
+
29
+ iframe.className = "qunit-subsuite";
30
+ body.appendChild( iframe );
31
+
32
+ function onIframeLoad() {
33
+ var module, test,
34
+ count = 0;
35
+
36
+ if (iframe.src === "") {
37
+ return;
38
+ }
39
+
40
+ iframeWin.QUnit.moduleStart(function( data ) {
41
+ // capture module name for messages
42
+ module = data.name;
43
+ });
44
+
45
+ iframeWin.QUnit.testStart(function( data ) {
46
+ // capture test name for messages
47
+ test = data.name;
48
+ });
49
+ iframeWin.QUnit.testDone(function() {
50
+ test = null;
51
+ });
52
+
53
+ iframeWin.QUnit.log(function( data ) {
54
+ if (test === null) {
55
+ return;
56
+ }
57
+ // pass all test details through to the main page
58
+ var message = module + ": " + test + ": " + data.message;
59
+ expect( ++count );
60
+ QUnit.push( data.result, data.actual, data.expected, message );
61
+ });
62
+
63
+ iframeWin.QUnit.done(function() {
64
+ // start the wrapper test from the main page
65
+ start();
66
+ });
67
+ }
68
+ QUnit.addEvent( iframe, "load", onIframeLoad );
69
+
70
+ iframeWin = iframe.contentWindow;
71
+ }
72
+ });
73
+
74
+ QUnit.testStart(function( data ) {
75
+ // update the test status to show which test suite is running
76
+ QUnit.id( "qunit-testresult" ).innerHTML = "Running " + data.name + "...<br>&nbsp;";
77
+ });
78
+
79
+ QUnit.testDone(function() {
80
+ var i,
81
+ current = QUnit.id( this.config.current.id ),
82
+ children = current.children,
83
+ src = this.iframe.src;
84
+
85
+ // undo the auto-expansion of failed tests
86
+ for ( i = 0; i < children.length; i++ ) {
87
+ if ( children[i].nodeName === "OL" ) {
88
+ children[i].style.display = "none";
89
+ }
90
+ }
91
+
92
+ QUnit.addEvent(current, "dblclick", function( e ) {
93
+ var target = e && e.target ? e.target : window.event.srcElement;
94
+ if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) {
95
+ target = target.parentNode;
96
+ }
97
+ if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
98
+ window.location = src;
99
+ }
100
+ });
101
+
102
+ current.getElementsByTagName('a')[0].href = src;
103
+ });
104
+
105
+ }( QUnit ) );
@@ -0,0 +1,1977 @@
1
+ /**
2
+ * QUnit v1.10.0 - A JavaScript Unit Testing Framework
3
+ *
4
+ * http://qunitjs.com
5
+ *
6
+ * Copyright 2012 jQuery Foundation and other contributors
7
+ * Released under the MIT license.
8
+ * http://jquery.org/license
9
+ */
10
+
11
+ (function( window ) {
12
+
13
+ var QUnit,
14
+ config,
15
+ onErrorFnPrev,
16
+ testId = 0,
17
+ fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""),
18
+ toString = Object.prototype.toString,
19
+ hasOwn = Object.prototype.hasOwnProperty,
20
+ // Keep a local reference to Date (GH-283)
21
+ Date = window.Date,
22
+ defined = {
23
+ setTimeout: typeof window.setTimeout !== "undefined",
24
+ sessionStorage: (function() {
25
+ var x = "qunit-test-string";
26
+ try {
27
+ sessionStorage.setItem( x, x );
28
+ sessionStorage.removeItem( x );
29
+ return true;
30
+ } catch( e ) {
31
+ return false;
32
+ }
33
+ }())
34
+ };
35
+
36
+ function Test( settings ) {
37
+ extend( this, settings );
38
+ this.assertions = [];
39
+ this.testNumber = ++Test.count;
40
+ }
41
+
42
+ Test.count = 0;
43
+
44
+ Test.prototype = {
45
+ init: function() {
46
+ var a, b, li,
47
+ tests = id( "qunit-tests" );
48
+
49
+ if ( tests ) {
50
+ b = document.createElement( "strong" );
51
+ b.innerHTML = this.name;
52
+
53
+ // `a` initialized at top of scope
54
+ a = document.createElement( "a" );
55
+ a.innerHTML = "Rerun";
56
+ a.href = QUnit.url({ testNumber: this.testNumber });
57
+
58
+ li = document.createElement( "li" );
59
+ li.appendChild( b );
60
+ li.appendChild( a );
61
+ li.className = "running";
62
+ li.id = this.id = "qunit-test-output" + testId++;
63
+
64
+ tests.appendChild( li );
65
+ }
66
+ },
67
+ setup: function() {
68
+ if ( this.module !== config.previousModule ) {
69
+ if ( config.previousModule ) {
70
+ runLoggingCallbacks( "moduleDone", QUnit, {
71
+ name: config.previousModule,
72
+ failed: config.moduleStats.bad,
73
+ passed: config.moduleStats.all - config.moduleStats.bad,
74
+ total: config.moduleStats.all
75
+ });
76
+ }
77
+ config.previousModule = this.module;
78
+ config.moduleStats = { all: 0, bad: 0 };
79
+ runLoggingCallbacks( "moduleStart", QUnit, {
80
+ name: this.module
81
+ });
82
+ } else if ( config.autorun ) {
83
+ runLoggingCallbacks( "moduleStart", QUnit, {
84
+ name: this.module
85
+ });
86
+ }
87
+
88
+ config.current = this;
89
+
90
+ this.testEnvironment = extend({
91
+ setup: function() {},
92
+ teardown: function() {}
93
+ }, this.moduleTestEnvironment );
94
+
95
+ runLoggingCallbacks( "testStart", QUnit, {
96
+ name: this.testName,
97
+ module: this.module
98
+ });
99
+
100
+ // allow utility functions to access the current test environment
101
+ // TODO why??
102
+ QUnit.current_testEnvironment = this.testEnvironment;
103
+
104
+ if ( !config.pollution ) {
105
+ saveGlobal();
106
+ }
107
+ if ( config.notrycatch ) {
108
+ this.testEnvironment.setup.call( this.testEnvironment );
109
+ return;
110
+ }
111
+ try {
112
+ this.testEnvironment.setup.call( this.testEnvironment );
113
+ } catch( e ) {
114
+ QUnit.pushFailure( "Setup failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) );
115
+ }
116
+ },
117
+ run: function() {
118
+ config.current = this;
119
+
120
+ var running = id( "qunit-testresult" );
121
+
122
+ if ( running ) {
123
+ running.innerHTML = "Running: <br/>" + this.name;
124
+ }
125
+
126
+ if ( this.async ) {
127
+ QUnit.stop();
128
+ }
129
+
130
+ if ( config.notrycatch ) {
131
+ this.callback.call( this.testEnvironment, QUnit.assert );
132
+ return;
133
+ }
134
+
135
+ try {
136
+ this.callback.call( this.testEnvironment, QUnit.assert );
137
+ } catch( e ) {
138
+ QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + e.message, extractStacktrace( e, 0 ) );
139
+ // else next test will carry the responsibility
140
+ saveGlobal();
141
+
142
+ // Restart the tests if they're blocking
143
+ if ( config.blocking ) {
144
+ QUnit.start();
145
+ }
146
+ }
147
+ },
148
+ teardown: function() {
149
+ config.current = this;
150
+ if ( config.notrycatch ) {
151
+ this.testEnvironment.teardown.call( this.testEnvironment );
152
+ return;
153
+ } else {
154
+ try {
155
+ this.testEnvironment.teardown.call( this.testEnvironment );
156
+ } catch( e ) {
157
+ QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) );
158
+ }
159
+ }
160
+ checkPollution();
161
+ },
162
+ finish: function() {
163
+ config.current = this;
164
+ if ( config.requireExpects && this.expected == null ) {
165
+ QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );
166
+ } else if ( this.expected != null && this.expected != this.assertions.length ) {
167
+ QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
168
+ } else if ( this.expected == null && !this.assertions.length ) {
169
+ QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );
170
+ }
171
+
172
+ var assertion, a, b, i, li, ol,
173
+ test = this,
174
+ good = 0,
175
+ bad = 0,
176
+ tests = id( "qunit-tests" );
177
+
178
+ config.stats.all += this.assertions.length;
179
+ config.moduleStats.all += this.assertions.length;
180
+
181
+ if ( tests ) {
182
+ ol = document.createElement( "ol" );
183
+
184
+ for ( i = 0; i < this.assertions.length; i++ ) {
185
+ assertion = this.assertions[i];
186
+
187
+ li = document.createElement( "li" );
188
+ li.className = assertion.result ? "pass" : "fail";
189
+ li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" );
190
+ ol.appendChild( li );
191
+
192
+ if ( assertion.result ) {
193
+ good++;
194
+ } else {
195
+ bad++;
196
+ config.stats.bad++;
197
+ config.moduleStats.bad++;
198
+ }
199
+ }
200
+
201
+ // store result when possible
202
+ if ( QUnit.config.reorder && defined.sessionStorage ) {
203
+ if ( bad ) {
204
+ sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad );
205
+ } else {
206
+ sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName );
207
+ }
208
+ }
209
+
210
+ if ( bad === 0 ) {
211
+ ol.style.display = "none";
212
+ }
213
+
214
+ // `b` initialized at top of scope
215
+ b = document.createElement( "strong" );
216
+ b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
217
+
218
+ addEvent(b, "click", function() {
219
+ var next = b.nextSibling.nextSibling,
220
+ display = next.style.display;
221
+ next.style.display = display === "none" ? "block" : "none";
222
+ });
223
+
224
+ addEvent(b, "dblclick", function( e ) {
225
+ var target = e && e.target ? e.target : window.event.srcElement;
226
+ if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
227
+ target = target.parentNode;
228
+ }
229
+ if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
230
+ window.location = QUnit.url({ testNumber: test.testNumber });
231
+ }
232
+ });
233
+
234
+ // `li` initialized at top of scope
235
+ li = id( this.id );
236
+ li.className = bad ? "fail" : "pass";
237
+ li.removeChild( li.firstChild );
238
+ a = li.firstChild;
239
+ li.appendChild( b );
240
+ li.appendChild ( a );
241
+ li.appendChild( ol );
242
+
243
+ } else {
244
+ for ( i = 0; i < this.assertions.length; i++ ) {
245
+ if ( !this.assertions[i].result ) {
246
+ bad++;
247
+ config.stats.bad++;
248
+ config.moduleStats.bad++;
249
+ }
250
+ }
251
+ }
252
+
253
+ runLoggingCallbacks( "testDone", QUnit, {
254
+ name: this.testName,
255
+ module: this.module,
256
+ failed: bad,
257
+ passed: this.assertions.length - bad,
258
+ total: this.assertions.length
259
+ });
260
+
261
+ QUnit.reset();
262
+
263
+ config.current = undefined;
264
+ },
265
+
266
+ queue: function() {
267
+ var bad,
268
+ test = this;
269
+
270
+ synchronize(function() {
271
+ test.init();
272
+ });
273
+ function run() {
274
+ // each of these can by async
275
+ synchronize(function() {
276
+ test.setup();
277
+ });
278
+ synchronize(function() {
279
+ test.run();
280
+ });
281
+ synchronize(function() {
282
+ test.teardown();
283
+ });
284
+ synchronize(function() {
285
+ test.finish();
286
+ });
287
+ }
288
+
289
+ // `bad` initialized at top of scope
290
+ // defer when previous test run passed, if storage is available
291
+ bad = QUnit.config.reorder && defined.sessionStorage &&
292
+ +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );
293
+
294
+ if ( bad ) {
295
+ run();
296
+ } else {
297
+ synchronize( run, true );
298
+ }
299
+ }
300
+ };
301
+
302
+ // Root QUnit object.
303
+ // `QUnit` initialized at top of scope
304
+ QUnit = {
305
+
306
+ // call on start of module test to prepend name to all tests
307
+ module: function( name, testEnvironment ) {
308
+ config.currentModule = name;
309
+ config.currentModuleTestEnvironment = testEnvironment;
310
+ config.modules[name] = true;
311
+ },
312
+
313
+ asyncTest: function( testName, expected, callback ) {
314
+ if ( arguments.length === 2 ) {
315
+ callback = expected;
316
+ expected = null;
317
+ }
318
+
319
+ QUnit.test( testName, expected, callback, true );
320
+ },
321
+
322
+ test: function( testName, expected, callback, async ) {
323
+ var test,
324
+ name = "<span class='test-name'>" + escapeInnerText( testName ) + "</span>";
325
+
326
+ if ( arguments.length === 2 ) {
327
+ callback = expected;
328
+ expected = null;
329
+ }
330
+
331
+ if ( config.currentModule ) {
332
+ name = "<span class='module-name'>" + config.currentModule + "</span>: " + name;
333
+ }
334
+
335
+ test = new Test({
336
+ name: name,
337
+ testName: testName,
338
+ expected: expected,
339
+ async: async,
340
+ callback: callback,
341
+ module: config.currentModule,
342
+ moduleTestEnvironment: config.currentModuleTestEnvironment,
343
+ stack: sourceFromStacktrace( 2 )
344
+ });
345
+
346
+ if ( !validTest( test ) ) {
347
+ return;
348
+ }
349
+
350
+ test.queue();
351
+ },
352
+
353
+ // Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
354
+ expect: function( asserts ) {
355
+ if (arguments.length === 1) {
356
+ config.current.expected = asserts;
357
+ } else {
358
+ return config.current.expected;
359
+ }
360
+ },
361
+
362
+ start: function( count ) {
363
+ config.semaphore -= count || 1;
364
+ // don't start until equal number of stop-calls
365
+ if ( config.semaphore > 0 ) {
366
+ return;
367
+ }
368
+ // ignore if start is called more often then stop
369
+ if ( config.semaphore < 0 ) {
370
+ config.semaphore = 0;
371
+ }
372
+ // A slight delay, to avoid any current callbacks
373
+ if ( defined.setTimeout ) {
374
+ window.setTimeout(function() {
375
+ if ( config.semaphore > 0 ) {
376
+ return;
377
+ }
378
+ if ( config.timeout ) {
379
+ clearTimeout( config.timeout );
380
+ }
381
+
382
+ config.blocking = false;
383
+ process( true );
384
+ }, 13);
385
+ } else {
386
+ config.blocking = false;
387
+ process( true );
388
+ }
389
+ },
390
+
391
+ stop: function( count ) {
392
+ config.semaphore += count || 1;
393
+ config.blocking = true;
394
+
395
+ if ( config.testTimeout && defined.setTimeout ) {
396
+ clearTimeout( config.timeout );
397
+ config.timeout = window.setTimeout(function() {
398
+ QUnit.ok( false, "Test timed out" );
399
+ config.semaphore = 1;
400
+ QUnit.start();
401
+ }, config.testTimeout );
402
+ }
403
+ }
404
+ };
405
+
406
+ // Asssert helpers
407
+ // All of these must call either QUnit.push() or manually do:
408
+ // - runLoggingCallbacks( "log", .. );
409
+ // - config.current.assertions.push({ .. });
410
+ QUnit.assert = {
411
+ /**
412
+ * Asserts rough true-ish result.
413
+ * @name ok
414
+ * @function
415
+ * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
416
+ */
417
+ ok: function( result, msg ) {
418
+ if ( !config.current ) {
419
+ throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) );
420
+ }
421
+ result = !!result;
422
+
423
+ var source,
424
+ details = {
425
+ module: config.current.module,
426
+ name: config.current.testName,
427
+ result: result,
428
+ message: msg
429
+ };
430
+
431
+ msg = escapeInnerText( msg || (result ? "okay" : "failed" ) );
432
+ msg = "<span class='test-message'>" + msg + "</span>";
433
+
434
+ if ( !result ) {
435
+ source = sourceFromStacktrace( 2 );
436
+ if ( source ) {
437
+ details.source = source;
438
+ msg += "<table><tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr></table>";
439
+ }
440
+ }
441
+ runLoggingCallbacks( "log", QUnit, details );
442
+ config.current.assertions.push({
443
+ result: result,
444
+ message: msg
445
+ });
446
+ },
447
+
448
+ /**
449
+ * Assert that the first two arguments are equal, with an optional message.
450
+ * Prints out both actual and expected values.
451
+ * @name equal
452
+ * @function
453
+ * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
454
+ */
455
+ equal: function( actual, expected, message ) {
456
+ QUnit.push( expected == actual, actual, expected, message );
457
+ },
458
+
459
+ /**
460
+ * @name notEqual
461
+ * @function
462
+ */
463
+ notEqual: function( actual, expected, message ) {
464
+ QUnit.push( expected != actual, actual, expected, message );
465
+ },
466
+
467
+ /**
468
+ * @name deepEqual
469
+ * @function
470
+ */
471
+ deepEqual: function( actual, expected, message ) {
472
+ QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
473
+ },
474
+
475
+ /**
476
+ * @name notDeepEqual
477
+ * @function
478
+ */
479
+ notDeepEqual: function( actual, expected, message ) {
480
+ QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
481
+ },
482
+
483
+ /**
484
+ * @name strictEqual
485
+ * @function
486
+ */
487
+ strictEqual: function( actual, expected, message ) {
488
+ QUnit.push( expected === actual, actual, expected, message );
489
+ },
490
+
491
+ /**
492
+ * @name notStrictEqual
493
+ * @function
494
+ */
495
+ notStrictEqual: function( actual, expected, message ) {
496
+ QUnit.push( expected !== actual, actual, expected, message );
497
+ },
498
+
499
+ throws: function( block, expected, message ) {
500
+ var actual,
501
+ ok = false;
502
+
503
+ // 'expected' is optional
504
+ if ( typeof expected === "string" ) {
505
+ message = expected;
506
+ expected = null;
507
+ }
508
+
509
+ config.current.ignoreGlobalErrors = true;
510
+ try {
511
+ block.call( config.current.testEnvironment );
512
+ } catch (e) {
513
+ actual = e;
514
+ }
515
+ config.current.ignoreGlobalErrors = false;
516
+
517
+ if ( actual ) {
518
+ // we don't want to validate thrown error
519
+ if ( !expected ) {
520
+ ok = true;
521
+ // expected is a regexp
522
+ } else if ( QUnit.objectType( expected ) === "regexp" ) {
523
+ ok = expected.test( actual );
524
+ // expected is a constructor
525
+ } else if ( actual instanceof expected ) {
526
+ ok = true;
527
+ // expected is a validation function which returns true is validation passed
528
+ } else if ( expected.call( {}, actual ) === true ) {
529
+ ok = true;
530
+ }
531
+
532
+ QUnit.push( ok, actual, null, message );
533
+ } else {
534
+ QUnit.pushFailure( message, null, 'No exception was thrown.' );
535
+ }
536
+ }
537
+ };
538
+
539
+ /**
540
+ * @deprecate since 1.8.0
541
+ * Kept assertion helpers in root for backwards compatibility
542
+ */
543
+ extend( QUnit, QUnit.assert );
544
+
545
+ /**
546
+ * @deprecated since 1.9.0
547
+ * Kept global "raises()" for backwards compatibility
548
+ */
549
+ QUnit.raises = QUnit.assert.throws;
550
+
551
+ /**
552
+ * @deprecated since 1.0.0, replaced with error pushes since 1.3.0
553
+ * Kept to avoid TypeErrors for undefined methods.
554
+ */
555
+ QUnit.equals = function() {
556
+ QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
557
+ };
558
+ QUnit.same = function() {
559
+ QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );
560
+ };
561
+
562
+ // We want access to the constructor's prototype
563
+ (function() {
564
+ function F() {}
565
+ F.prototype = QUnit;
566
+ QUnit = new F();
567
+ // Make F QUnit's constructor so that we can add to the prototype later
568
+ QUnit.constructor = F;
569
+ }());
570
+
571
+ /**
572
+ * Config object: Maintain internal state
573
+ * Later exposed as QUnit.config
574
+ * `config` initialized at top of scope
575
+ */
576
+ config = {
577
+ // The queue of tests to run
578
+ queue: [],
579
+
580
+ // block until document ready
581
+ blocking: true,
582
+
583
+ // when enabled, show only failing tests
584
+ // gets persisted through sessionStorage and can be changed in UI via checkbox
585
+ hidepassed: false,
586
+
587
+ // by default, run previously failed tests first
588
+ // very useful in combination with "Hide passed tests" checked
589
+ reorder: true,
590
+
591
+ // by default, modify document.title when suite is done
592
+ altertitle: true,
593
+
594
+ // when enabled, all tests must call expect()
595
+ requireExpects: false,
596
+
597
+ // add checkboxes that are persisted in the query-string
598
+ // when enabled, the id is set to `true` as a `QUnit.config` property
599
+ urlConfig: [
600
+ {
601
+ id: "noglobals",
602
+ label: "Check for Globals",
603
+ tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings."
604
+ },
605
+ {
606
+ id: "notrycatch",
607
+ label: "No try-catch",
608
+ tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings."
609
+ }
610
+ ],
611
+
612
+ // Set of all modules.
613
+ modules: {},
614
+
615
+ // logging callback queues
616
+ begin: [],
617
+ done: [],
618
+ log: [],
619
+ testStart: [],
620
+ testDone: [],
621
+ moduleStart: [],
622
+ moduleDone: []
623
+ };
624
+
625
+ // Initialize more QUnit.config and QUnit.urlParams
626
+ (function() {
627
+ var i,
628
+ location = window.location || { search: "", protocol: "file:" },
629
+ params = location.search.slice( 1 ).split( "&" ),
630
+ length = params.length,
631
+ urlParams = {},
632
+ current;
633
+
634
+ if ( params[ 0 ] ) {
635
+ for ( i = 0; i < length; i++ ) {
636
+ current = params[ i ].split( "=" );
637
+ current[ 0 ] = decodeURIComponent( current[ 0 ] );
638
+ // allow just a key to turn on a flag, e.g., test.html?noglobals
639
+ current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
640
+ urlParams[ current[ 0 ] ] = current[ 1 ];
641
+ }
642
+ }
643
+
644
+ QUnit.urlParams = urlParams;
645
+
646
+ // String search anywhere in moduleName+testName
647
+ config.filter = urlParams.filter;
648
+
649
+ // Exact match of the module name
650
+ config.module = urlParams.module;
651
+
652
+ config.testNumber = parseInt( urlParams.testNumber, 10 ) || null;
653
+
654
+ // Figure out if we're running the tests from a server or not
655
+ QUnit.isLocal = location.protocol === "file:";
656
+ }());
657
+
658
+ // Export global variables, unless an 'exports' object exists,
659
+ // in that case we assume we're in CommonJS (dealt with on the bottom of the script)
660
+ if ( typeof exports === "undefined" ) {
661
+ extend( window, QUnit );
662
+
663
+ // Expose QUnit object
664
+ window.QUnit = QUnit;
665
+ }
666
+
667
+ // Extend QUnit object,
668
+ // these after set here because they should not be exposed as global functions
669
+ extend( QUnit, {
670
+ config: config,
671
+
672
+ // Initialize the configuration options
673
+ init: function() {
674
+ extend( config, {
675
+ stats: { all: 0, bad: 0 },
676
+ moduleStats: { all: 0, bad: 0 },
677
+ started: +new Date(),
678
+ updateRate: 1000,
679
+ blocking: false,
680
+ autostart: true,
681
+ autorun: false,
682
+ filter: "",
683
+ queue: [],
684
+ semaphore: 0
685
+ });
686
+
687
+ var tests, banner, result,
688
+ qunit = id( "qunit" );
689
+
690
+ if ( qunit ) {
691
+ qunit.innerHTML =
692
+ "<h1 id='qunit-header'>" + escapeInnerText( document.title ) + "</h1>" +
693
+ "<h2 id='qunit-banner'></h2>" +
694
+ "<div id='qunit-testrunner-toolbar'></div>" +
695
+ "<h2 id='qunit-userAgent'></h2>" +
696
+ "<ol id='qunit-tests'></ol>";
697
+ }
698
+
699
+ tests = id( "qunit-tests" );
700
+ banner = id( "qunit-banner" );
701
+ result = id( "qunit-testresult" );
702
+
703
+ if ( tests ) {
704
+ tests.innerHTML = "";
705
+ }
706
+
707
+ if ( banner ) {
708
+ banner.className = "";
709
+ }
710
+
711
+ if ( result ) {
712
+ result.parentNode.removeChild( result );
713
+ }
714
+
715
+ if ( tests ) {
716
+ result = document.createElement( "p" );
717
+ result.id = "qunit-testresult";
718
+ result.className = "result";
719
+ tests.parentNode.insertBefore( result, tests );
720
+ result.innerHTML = "Running...<br/>&nbsp;";
721
+ }
722
+ },
723
+
724
+ // Resets the test setup. Useful for tests that modify the DOM.
725
+ reset: function() {
726
+ var fixture = id( "qunit-fixture" );
727
+ if ( fixture ) {
728
+ fixture.innerHTML = config.fixture;
729
+ }
730
+ },
731
+
732
+ // Trigger an event on an element.
733
+ // @example triggerEvent( document.body, "click" );
734
+ triggerEvent: function( elem, type, event ) {
735
+ if ( document.createEvent ) {
736
+ event = document.createEvent( "MouseEvents" );
737
+ event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
738
+ 0, 0, 0, 0, 0, false, false, false, false, 0, null);
739
+
740
+ elem.dispatchEvent( event );
741
+ } else if ( elem.fireEvent ) {
742
+ elem.fireEvent( "on" + type );
743
+ }
744
+ },
745
+
746
+ // Safe object type checking
747
+ is: function( type, obj ) {
748
+ return QUnit.objectType( obj ) == type;
749
+ },
750
+
751
+ objectType: function( obj ) {
752
+ if ( typeof obj === "undefined" ) {
753
+ return "undefined";
754
+ // consider: typeof null === object
755
+ }
756
+ if ( obj === null ) {
757
+ return "null";
758
+ }
759
+
760
+ var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || "";
761
+
762
+ switch ( type ) {
763
+ case "Number":
764
+ if ( isNaN(obj) ) {
765
+ return "nan";
766
+ }
767
+ return "number";
768
+ case "String":
769
+ case "Boolean":
770
+ case "Array":
771
+ case "Date":
772
+ case "RegExp":
773
+ case "Function":
774
+ return type.toLowerCase();
775
+ }
776
+ if ( typeof obj === "object" ) {
777
+ return "object";
778
+ }
779
+ return undefined;
780
+ },
781
+
782
+ push: function( result, actual, expected, message ) {
783
+ if ( !config.current ) {
784
+ throw new Error( "assertion outside test context, was " + sourceFromStacktrace() );
785
+ }
786
+
787
+ var output, source,
788
+ details = {
789
+ module: config.current.module,
790
+ name: config.current.testName,
791
+ result: result,
792
+ message: message,
793
+ actual: actual,
794
+ expected: expected
795
+ };
796
+
797
+ message = escapeInnerText( message ) || ( result ? "okay" : "failed" );
798
+ message = "<span class='test-message'>" + message + "</span>";
799
+ output = message;
800
+
801
+ if ( !result ) {
802
+ expected = escapeInnerText( QUnit.jsDump.parse(expected) );
803
+ actual = escapeInnerText( QUnit.jsDump.parse(actual) );
804
+ output += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + expected + "</pre></td></tr>";
805
+
806
+ if ( actual != expected ) {
807
+ output += "<tr class='test-actual'><th>Result: </th><td><pre>" + actual + "</pre></td></tr>";
808
+ output += "<tr class='test-diff'><th>Diff: </th><td><pre>" + QUnit.diff( expected, actual ) + "</pre></td></tr>";
809
+ }
810
+
811
+ source = sourceFromStacktrace();
812
+
813
+ if ( source ) {
814
+ details.source = source;
815
+ output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr>";
816
+ }
817
+
818
+ output += "</table>";
819
+ }
820
+
821
+ runLoggingCallbacks( "log", QUnit, details );
822
+
823
+ config.current.assertions.push({
824
+ result: !!result,
825
+ message: output
826
+ });
827
+ },
828
+
829
+ pushFailure: function( message, source, actual ) {
830
+ if ( !config.current ) {
831
+ throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) );
832
+ }
833
+
834
+ var output,
835
+ details = {
836
+ module: config.current.module,
837
+ name: config.current.testName,
838
+ result: false,
839
+ message: message
840
+ };
841
+
842
+ message = escapeInnerText( message ) || "error";
843
+ message = "<span class='test-message'>" + message + "</span>";
844
+ output = message;
845
+
846
+ output += "<table>";
847
+
848
+ if ( actual ) {
849
+ output += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeInnerText( actual ) + "</pre></td></tr>";
850
+ }
851
+
852
+ if ( source ) {
853
+ details.source = source;
854
+ output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr>";
855
+ }
856
+
857
+ output += "</table>";
858
+
859
+ runLoggingCallbacks( "log", QUnit, details );
860
+
861
+ config.current.assertions.push({
862
+ result: false,
863
+ message: output
864
+ });
865
+ },
866
+
867
+ url: function( params ) {
868
+ params = extend( extend( {}, QUnit.urlParams ), params );
869
+ var key,
870
+ querystring = "?";
871
+
872
+ for ( key in params ) {
873
+ if ( !hasOwn.call( params, key ) ) {
874
+ continue;
875
+ }
876
+ querystring += encodeURIComponent( key ) + "=" +
877
+ encodeURIComponent( params[ key ] ) + "&";
878
+ }
879
+ return window.location.pathname + querystring.slice( 0, -1 );
880
+ },
881
+
882
+ extend: extend,
883
+ id: id,
884
+ addEvent: addEvent
885
+ // load, equiv, jsDump, diff: Attached later
886
+ });
887
+
888
+ /**
889
+ * @deprecated: Created for backwards compatibility with test runner that set the hook function
890
+ * into QUnit.{hook}, instead of invoking it and passing the hook function.
891
+ * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.
892
+ * Doing this allows us to tell if the following methods have been overwritten on the actual
893
+ * QUnit object.
894
+ */
895
+ extend( QUnit.constructor.prototype, {
896
+
897
+ // Logging callbacks; all receive a single argument with the listed properties
898
+ // run test/logs.html for any related changes
899
+ begin: registerLoggingCallback( "begin" ),
900
+
901
+ // done: { failed, passed, total, runtime }
902
+ done: registerLoggingCallback( "done" ),
903
+
904
+ // log: { result, actual, expected, message }
905
+ log: registerLoggingCallback( "log" ),
906
+
907
+ // testStart: { name }
908
+ testStart: registerLoggingCallback( "testStart" ),
909
+
910
+ // testDone: { name, failed, passed, total }
911
+ testDone: registerLoggingCallback( "testDone" ),
912
+
913
+ // moduleStart: { name }
914
+ moduleStart: registerLoggingCallback( "moduleStart" ),
915
+
916
+ // moduleDone: { name, failed, passed, total }
917
+ moduleDone: registerLoggingCallback( "moduleDone" )
918
+ });
919
+
920
+ if ( typeof document === "undefined" || document.readyState === "complete" ) {
921
+ config.autorun = true;
922
+ }
923
+
924
+ QUnit.load = function() {
925
+ runLoggingCallbacks( "begin", QUnit, {} );
926
+
927
+ // Initialize the config, saving the execution queue
928
+ var banner, filter, i, label, len, main, ol, toolbar, userAgent, val, urlConfigCheckboxes, moduleFilter,
929
+ numModules = 0,
930
+ moduleFilterHtml = "",
931
+ urlConfigHtml = "",
932
+ oldconfig = extend( {}, config );
933
+
934
+ QUnit.init();
935
+ extend(config, oldconfig);
936
+
937
+ config.blocking = false;
938
+
939
+ len = config.urlConfig.length;
940
+
941
+ for ( i = 0; i < len; i++ ) {
942
+ val = config.urlConfig[i];
943
+ if ( typeof val === "string" ) {
944
+ val = {
945
+ id: val,
946
+ label: val,
947
+ tooltip: "[no tooltip available]"
948
+ };
949
+ }
950
+ config[ val.id ] = QUnit.urlParams[ val.id ];
951
+ urlConfigHtml += "<input id='qunit-urlconfig-" + val.id + "' name='" + val.id + "' type='checkbox'" + ( config[ val.id ] ? " checked='checked'" : "" ) + " title='" + val.tooltip + "'><label for='qunit-urlconfig-" + val.id + "' title='" + val.tooltip + "'>" + val.label + "</label>";
952
+ }
953
+
954
+ moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label><select id='qunit-modulefilter' name='modulefilter'><option value='' " + ( config.module === undefined ? "selected" : "" ) + ">< All Modules ></option>";
955
+ for ( i in config.modules ) {
956
+ if ( config.modules.hasOwnProperty( i ) ) {
957
+ numModules += 1;
958
+ moduleFilterHtml += "<option value='" + encodeURIComponent(i) + "' " + ( config.module === i ? "selected" : "" ) + ">" + i + "</option>";
959
+ }
960
+ }
961
+ moduleFilterHtml += "</select>";
962
+
963
+ // `userAgent` initialized at top of scope
964
+ userAgent = id( "qunit-userAgent" );
965
+ if ( userAgent ) {
966
+ userAgent.innerHTML = navigator.userAgent;
967
+ }
968
+
969
+ // `banner` initialized at top of scope
970
+ banner = id( "qunit-header" );
971
+ if ( banner ) {
972
+ banner.innerHTML = "<a href='" + QUnit.url({ filter: undefined, module: undefined, testNumber: undefined }) + "'>" + banner.innerHTML + "</a> ";
973
+ }
974
+
975
+ // `toolbar` initialized at top of scope
976
+ toolbar = id( "qunit-testrunner-toolbar" );
977
+ if ( toolbar ) {
978
+ // `filter` initialized at top of scope
979
+ filter = document.createElement( "input" );
980
+ filter.type = "checkbox";
981
+ filter.id = "qunit-filter-pass";
982
+
983
+ addEvent( filter, "click", function() {
984
+ var tmp,
985
+ ol = document.getElementById( "qunit-tests" );
986
+
987
+ if ( filter.checked ) {
988
+ ol.className = ol.className + " hidepass";
989
+ } else {
990
+ tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
991
+ ol.className = tmp.replace( / hidepass /, " " );
992
+ }
993
+ if ( defined.sessionStorage ) {
994
+ if (filter.checked) {
995
+ sessionStorage.setItem( "qunit-filter-passed-tests", "true" );
996
+ } else {
997
+ sessionStorage.removeItem( "qunit-filter-passed-tests" );
998
+ }
999
+ }
1000
+ });
1001
+
1002
+ if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) {
1003
+ filter.checked = true;
1004
+ // `ol` initialized at top of scope
1005
+ ol = document.getElementById( "qunit-tests" );
1006
+ ol.className = ol.className + " hidepass";
1007
+ }
1008
+ toolbar.appendChild( filter );
1009
+
1010
+ // `label` initialized at top of scope
1011
+ label = document.createElement( "label" );
1012
+ label.setAttribute( "for", "qunit-filter-pass" );
1013
+ label.setAttribute( "title", "Only show tests and assertons that fail. Stored in sessionStorage." );
1014
+ label.innerHTML = "Hide passed tests";
1015
+ toolbar.appendChild( label );
1016
+
1017
+ urlConfigCheckboxes = document.createElement( 'span' );
1018
+ urlConfigCheckboxes.innerHTML = urlConfigHtml;
1019
+ addEvent( urlConfigCheckboxes, "change", function( event ) {
1020
+ var params = {};
1021
+ params[ event.target.name ] = event.target.checked ? true : undefined;
1022
+ window.location = QUnit.url( params );
1023
+ });
1024
+ toolbar.appendChild( urlConfigCheckboxes );
1025
+
1026
+ if (numModules > 1) {
1027
+ moduleFilter = document.createElement( 'span' );
1028
+ moduleFilter.setAttribute( 'id', 'qunit-modulefilter-container' );
1029
+ moduleFilter.innerHTML = moduleFilterHtml;
1030
+ addEvent( moduleFilter, "change", function() {
1031
+ var selectBox = moduleFilter.getElementsByTagName("select")[0],
1032
+ selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value);
1033
+
1034
+ window.location = QUnit.url( { module: ( selectedModule === "" ) ? undefined : selectedModule } );
1035
+ });
1036
+ toolbar.appendChild(moduleFilter);
1037
+ }
1038
+ }
1039
+
1040
+ // `main` initialized at top of scope
1041
+ main = id( "qunit-fixture" );
1042
+ if ( main ) {
1043
+ config.fixture = main.innerHTML;
1044
+ }
1045
+
1046
+ if ( config.autostart ) {
1047
+ QUnit.start();
1048
+ }
1049
+ };
1050
+
1051
+ addEvent( window, "load", QUnit.load );
1052
+
1053
+ // `onErrorFnPrev` initialized at top of scope
1054
+ // Preserve other handlers
1055
+ onErrorFnPrev = window.onerror;
1056
+
1057
+ // Cover uncaught exceptions
1058
+ // Returning true will surpress the default browser handler,
1059
+ // returning false will let it run.
1060
+ window.onerror = function ( error, filePath, linerNr ) {
1061
+ var ret = false;
1062
+ if ( onErrorFnPrev ) {
1063
+ ret = onErrorFnPrev( error, filePath, linerNr );
1064
+ }
1065
+
1066
+ // Treat return value as window.onerror itself does,
1067
+ // Only do our handling if not surpressed.
1068
+ if ( ret !== true ) {
1069
+ if ( QUnit.config.current ) {
1070
+ if ( QUnit.config.current.ignoreGlobalErrors ) {
1071
+ return true;
1072
+ }
1073
+ QUnit.pushFailure( error, filePath + ":" + linerNr );
1074
+ } else {
1075
+ QUnit.test( "global failure", extend( function() {
1076
+ QUnit.pushFailure( error, filePath + ":" + linerNr );
1077
+ }, { validTest: validTest } ) );
1078
+ }
1079
+ return false;
1080
+ }
1081
+
1082
+ return ret;
1083
+ };
1084
+
1085
+ function done() {
1086
+ config.autorun = true;
1087
+
1088
+ // Log the last module results
1089
+ if ( config.currentModule ) {
1090
+ runLoggingCallbacks( "moduleDone", QUnit, {
1091
+ name: config.currentModule,
1092
+ failed: config.moduleStats.bad,
1093
+ passed: config.moduleStats.all - config.moduleStats.bad,
1094
+ total: config.moduleStats.all
1095
+ });
1096
+ }
1097
+
1098
+ var i, key,
1099
+ banner = id( "qunit-banner" ),
1100
+ tests = id( "qunit-tests" ),
1101
+ runtime = +new Date() - config.started,
1102
+ passed = config.stats.all - config.stats.bad,
1103
+ html = [
1104
+ "Tests completed in ",
1105
+ runtime,
1106
+ " milliseconds.<br/>",
1107
+ "<span class='passed'>",
1108
+ passed,
1109
+ "</span> tests of <span class='total'>",
1110
+ config.stats.all,
1111
+ "</span> passed, <span class='failed'>",
1112
+ config.stats.bad,
1113
+ "</span> failed."
1114
+ ].join( "" );
1115
+
1116
+ if ( banner ) {
1117
+ banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" );
1118
+ }
1119
+
1120
+ if ( tests ) {
1121
+ id( "qunit-testresult" ).innerHTML = html;
1122
+ }
1123
+
1124
+ if ( config.altertitle && typeof document !== "undefined" && document.title ) {
1125
+ // show ✖ for good, ✔ for bad suite result in title
1126
+ // use escape sequences in case file gets loaded with non-utf-8-charset
1127
+ document.title = [
1128
+ ( config.stats.bad ? "\u2716" : "\u2714" ),
1129
+ document.title.replace( /^[\u2714\u2716] /i, "" )
1130
+ ].join( " " );
1131
+ }
1132
+
1133
+ // clear own sessionStorage items if all tests passed
1134
+ if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) {
1135
+ // `key` & `i` initialized at top of scope
1136
+ for ( i = 0; i < sessionStorage.length; i++ ) {
1137
+ key = sessionStorage.key( i++ );
1138
+ if ( key.indexOf( "qunit-test-" ) === 0 ) {
1139
+ sessionStorage.removeItem( key );
1140
+ }
1141
+ }
1142
+ }
1143
+
1144
+ // scroll back to top to show results
1145
+ if ( window.scrollTo ) {
1146
+ window.scrollTo(0, 0);
1147
+ }
1148
+
1149
+ runLoggingCallbacks( "done", QUnit, {
1150
+ failed: config.stats.bad,
1151
+ passed: passed,
1152
+ total: config.stats.all,
1153
+ runtime: runtime
1154
+ });
1155
+ }
1156
+
1157
+ /** @return Boolean: true if this test should be ran */
1158
+ function validTest( test ) {
1159
+ var include,
1160
+ filter = config.filter && config.filter.toLowerCase(),
1161
+ module = config.module && config.module.toLowerCase(),
1162
+ fullName = (test.module + ": " + test.testName).toLowerCase();
1163
+
1164
+ // Internally-generated tests are always valid
1165
+ if ( test.callback && test.callback.validTest === validTest ) {
1166
+ delete test.callback.validTest;
1167
+ return true;
1168
+ }
1169
+
1170
+ if ( config.testNumber ) {
1171
+ return test.testNumber === config.testNumber;
1172
+ }
1173
+
1174
+ if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) {
1175
+ return false;
1176
+ }
1177
+
1178
+ if ( !filter ) {
1179
+ return true;
1180
+ }
1181
+
1182
+ include = filter.charAt( 0 ) !== "!";
1183
+ if ( !include ) {
1184
+ filter = filter.slice( 1 );
1185
+ }
1186
+
1187
+ // If the filter matches, we need to honour include
1188
+ if ( fullName.indexOf( filter ) !== -1 ) {
1189
+ return include;
1190
+ }
1191
+
1192
+ // Otherwise, do the opposite
1193
+ return !include;
1194
+ }
1195
+
1196
+ // so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions)
1197
+ // Later Safari and IE10 are supposed to support error.stack as well
1198
+ // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
1199
+ function extractStacktrace( e, offset ) {
1200
+ offset = offset === undefined ? 3 : offset;
1201
+
1202
+ var stack, include, i, regex;
1203
+
1204
+ if ( e.stacktrace ) {
1205
+ // Opera
1206
+ return e.stacktrace.split( "\n" )[ offset + 3 ];
1207
+ } else if ( e.stack ) {
1208
+ // Firefox, Chrome
1209
+ stack = e.stack.split( "\n" );
1210
+ if (/^error$/i.test( stack[0] ) ) {
1211
+ stack.shift();
1212
+ }
1213
+ if ( fileName ) {
1214
+ include = [];
1215
+ for ( i = offset; i < stack.length; i++ ) {
1216
+ if ( stack[ i ].indexOf( fileName ) != -1 ) {
1217
+ break;
1218
+ }
1219
+ include.push( stack[ i ] );
1220
+ }
1221
+ if ( include.length ) {
1222
+ return include.join( "\n" );
1223
+ }
1224
+ }
1225
+ return stack[ offset ];
1226
+ } else if ( e.sourceURL ) {
1227
+ // Safari, PhantomJS
1228
+ // hopefully one day Safari provides actual stacktraces
1229
+ // exclude useless self-reference for generated Error objects
1230
+ if ( /qunit.js$/.test( e.sourceURL ) ) {
1231
+ return;
1232
+ }
1233
+ // for actual exceptions, this is useful
1234
+ return e.sourceURL + ":" + e.line;
1235
+ }
1236
+ }
1237
+ function sourceFromStacktrace( offset ) {
1238
+ try {
1239
+ throw new Error();
1240
+ } catch ( e ) {
1241
+ return extractStacktrace( e, offset );
1242
+ }
1243
+ }
1244
+
1245
+ function escapeInnerText( s ) {
1246
+ if ( !s ) {
1247
+ return "";
1248
+ }
1249
+ s = s + "";
1250
+ return s.replace( /[\&<>]/g, function( s ) {
1251
+ switch( s ) {
1252
+ case "&": return "&amp;";
1253
+ case "<": return "&lt;";
1254
+ case ">": return "&gt;";
1255
+ default: return s;
1256
+ }
1257
+ });
1258
+ }
1259
+
1260
+ function synchronize( callback, last ) {
1261
+ config.queue.push( callback );
1262
+
1263
+ if ( config.autorun && !config.blocking ) {
1264
+ process( last );
1265
+ }
1266
+ }
1267
+
1268
+ function process( last ) {
1269
+ function next() {
1270
+ process( last );
1271
+ }
1272
+ var start = new Date().getTime();
1273
+ config.depth = config.depth ? config.depth + 1 : 1;
1274
+
1275
+ while ( config.queue.length && !config.blocking ) {
1276
+ if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
1277
+ config.queue.shift()();
1278
+ } else {
1279
+ window.setTimeout( next, 13 );
1280
+ break;
1281
+ }
1282
+ }
1283
+ config.depth--;
1284
+ if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
1285
+ done();
1286
+ }
1287
+ }
1288
+
1289
+ function saveGlobal() {
1290
+ config.pollution = [];
1291
+
1292
+ if ( config.noglobals ) {
1293
+ for ( var key in window ) {
1294
+ // in Opera sometimes DOM element ids show up here, ignore them
1295
+ if ( !hasOwn.call( window, key ) || /^qunit-test-output/.test( key ) ) {
1296
+ continue;
1297
+ }
1298
+ config.pollution.push( key );
1299
+ }
1300
+ }
1301
+ }
1302
+
1303
+ function checkPollution( name ) {
1304
+ var newGlobals,
1305
+ deletedGlobals,
1306
+ old = config.pollution;
1307
+
1308
+ saveGlobal();
1309
+
1310
+ newGlobals = diff( config.pollution, old );
1311
+ if ( newGlobals.length > 0 ) {
1312
+ QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") );
1313
+ }
1314
+
1315
+ deletedGlobals = diff( old, config.pollution );
1316
+ if ( deletedGlobals.length > 0 ) {
1317
+ QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") );
1318
+ }
1319
+ }
1320
+
1321
+ // returns a new Array with the elements that are in a but not in b
1322
+ function diff( a, b ) {
1323
+ var i, j,
1324
+ result = a.slice();
1325
+
1326
+ for ( i = 0; i < result.length; i++ ) {
1327
+ for ( j = 0; j < b.length; j++ ) {
1328
+ if ( result[i] === b[j] ) {
1329
+ result.splice( i, 1 );
1330
+ i--;
1331
+ break;
1332
+ }
1333
+ }
1334
+ }
1335
+ return result;
1336
+ }
1337
+
1338
+ function extend( a, b ) {
1339
+ for ( var prop in b ) {
1340
+ if ( b[ prop ] === undefined ) {
1341
+ delete a[ prop ];
1342
+
1343
+ // Avoid "Member not found" error in IE8 caused by setting window.constructor
1344
+ } else if ( prop !== "constructor" || a !== window ) {
1345
+ a[ prop ] = b[ prop ];
1346
+ }
1347
+ }
1348
+
1349
+ return a;
1350
+ }
1351
+
1352
+ function addEvent( elem, type, fn ) {
1353
+ if ( elem.addEventListener ) {
1354
+ elem.addEventListener( type, fn, false );
1355
+ } else if ( elem.attachEvent ) {
1356
+ elem.attachEvent( "on" + type, fn );
1357
+ } else {
1358
+ fn();
1359
+ }
1360
+ }
1361
+
1362
+ function id( name ) {
1363
+ return !!( typeof document !== "undefined" && document && document.getElementById ) &&
1364
+ document.getElementById( name );
1365
+ }
1366
+
1367
+ function registerLoggingCallback( key ) {
1368
+ return function( callback ) {
1369
+ config[key].push( callback );
1370
+ };
1371
+ }
1372
+
1373
+ // Supports deprecated method of completely overwriting logging callbacks
1374
+ function runLoggingCallbacks( key, scope, args ) {
1375
+ //debugger;
1376
+ var i, callbacks;
1377
+ if ( QUnit.hasOwnProperty( key ) ) {
1378
+ QUnit[ key ].call(scope, args );
1379
+ } else {
1380
+ callbacks = config[ key ];
1381
+ for ( i = 0; i < callbacks.length; i++ ) {
1382
+ callbacks[ i ].call( scope, args );
1383
+ }
1384
+ }
1385
+ }
1386
+
1387
+ // Test for equality any JavaScript type.
1388
+ // Author: Philippe Rathé <prathe@gmail.com>
1389
+ QUnit.equiv = (function() {
1390
+
1391
+ // Call the o related callback with the given arguments.
1392
+ function bindCallbacks( o, callbacks, args ) {
1393
+ var prop = QUnit.objectType( o );
1394
+ if ( prop ) {
1395
+ if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
1396
+ return callbacks[ prop ].apply( callbacks, args );
1397
+ } else {
1398
+ return callbacks[ prop ]; // or undefined
1399
+ }
1400
+ }
1401
+ }
1402
+
1403
+ // the real equiv function
1404
+ var innerEquiv,
1405
+ // stack to decide between skip/abort functions
1406
+ callers = [],
1407
+ // stack to avoiding loops from circular referencing
1408
+ parents = [],
1409
+
1410
+ getProto = Object.getPrototypeOf || function ( obj ) {
1411
+ return obj.__proto__;
1412
+ },
1413
+ callbacks = (function () {
1414
+
1415
+ // for string, boolean, number and null
1416
+ function useStrictEquality( b, a ) {
1417
+ if ( b instanceof a.constructor || a instanceof b.constructor ) {
1418
+ // to catch short annotaion VS 'new' annotation of a
1419
+ // declaration
1420
+ // e.g. var i = 1;
1421
+ // var j = new Number(1);
1422
+ return a == b;
1423
+ } else {
1424
+ return a === b;
1425
+ }
1426
+ }
1427
+
1428
+ return {
1429
+ "string": useStrictEquality,
1430
+ "boolean": useStrictEquality,
1431
+ "number": useStrictEquality,
1432
+ "null": useStrictEquality,
1433
+ "undefined": useStrictEquality,
1434
+
1435
+ "nan": function( b ) {
1436
+ return isNaN( b );
1437
+ },
1438
+
1439
+ "date": function( b, a ) {
1440
+ return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
1441
+ },
1442
+
1443
+ "regexp": function( b, a ) {
1444
+ return QUnit.objectType( b ) === "regexp" &&
1445
+ // the regex itself
1446
+ a.source === b.source &&
1447
+ // and its modifers
1448
+ a.global === b.global &&
1449
+ // (gmi) ...
1450
+ a.ignoreCase === b.ignoreCase &&
1451
+ a.multiline === b.multiline &&
1452
+ a.sticky === b.sticky;
1453
+ },
1454
+
1455
+ // - skip when the property is a method of an instance (OOP)
1456
+ // - abort otherwise,
1457
+ // initial === would have catch identical references anyway
1458
+ "function": function() {
1459
+ var caller = callers[callers.length - 1];
1460
+ return caller !== Object && typeof caller !== "undefined";
1461
+ },
1462
+
1463
+ "array": function( b, a ) {
1464
+ var i, j, len, loop;
1465
+
1466
+ // b could be an object literal here
1467
+ if ( QUnit.objectType( b ) !== "array" ) {
1468
+ return false;
1469
+ }
1470
+
1471
+ len = a.length;
1472
+ if ( len !== b.length ) {
1473
+ // safe and faster
1474
+ return false;
1475
+ }
1476
+
1477
+ // track reference to avoid circular references
1478
+ parents.push( a );
1479
+ for ( i = 0; i < len; i++ ) {
1480
+ loop = false;
1481
+ for ( j = 0; j < parents.length; j++ ) {
1482
+ if ( parents[j] === a[i] ) {
1483
+ loop = true;// dont rewalk array
1484
+ }
1485
+ }
1486
+ if ( !loop && !innerEquiv(a[i], b[i]) ) {
1487
+ parents.pop();
1488
+ return false;
1489
+ }
1490
+ }
1491
+ parents.pop();
1492
+ return true;
1493
+ },
1494
+
1495
+ "object": function( b, a ) {
1496
+ var i, j, loop,
1497
+ // Default to true
1498
+ eq = true,
1499
+ aProperties = [],
1500
+ bProperties = [];
1501
+
1502
+ // comparing constructors is more strict than using
1503
+ // instanceof
1504
+ if ( a.constructor !== b.constructor ) {
1505
+ // Allow objects with no prototype to be equivalent to
1506
+ // objects with Object as their constructor.
1507
+ if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) ||
1508
+ ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) {
1509
+ return false;
1510
+ }
1511
+ }
1512
+
1513
+ // stack constructor before traversing properties
1514
+ callers.push( a.constructor );
1515
+ // track reference to avoid circular references
1516
+ parents.push( a );
1517
+
1518
+ for ( i in a ) { // be strict: don't ensures hasOwnProperty
1519
+ // and go deep
1520
+ loop = false;
1521
+ for ( j = 0; j < parents.length; j++ ) {
1522
+ if ( parents[j] === a[i] ) {
1523
+ // don't go down the same path twice
1524
+ loop = true;
1525
+ }
1526
+ }
1527
+ aProperties.push(i); // collect a's properties
1528
+
1529
+ if (!loop && !innerEquiv( a[i], b[i] ) ) {
1530
+ eq = false;
1531
+ break;
1532
+ }
1533
+ }
1534
+
1535
+ callers.pop(); // unstack, we are done
1536
+ parents.pop();
1537
+
1538
+ for ( i in b ) {
1539
+ bProperties.push( i ); // collect b's properties
1540
+ }
1541
+
1542
+ // Ensures identical properties name
1543
+ return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
1544
+ }
1545
+ };
1546
+ }());
1547
+
1548
+ innerEquiv = function() { // can take multiple arguments
1549
+ var args = [].slice.apply( arguments );
1550
+ if ( args.length < 2 ) {
1551
+ return true; // end transition
1552
+ }
1553
+
1554
+ return (function( a, b ) {
1555
+ if ( a === b ) {
1556
+ return true; // catch the most you can
1557
+ } else if ( a === null || b === null || typeof a === "undefined" ||
1558
+ typeof b === "undefined" ||
1559
+ QUnit.objectType(a) !== QUnit.objectType(b) ) {
1560
+ return false; // don't lose time with error prone cases
1561
+ } else {
1562
+ return bindCallbacks(a, callbacks, [ b, a ]);
1563
+ }
1564
+
1565
+ // apply transition with (1..n) arguments
1566
+ }( args[0], args[1] ) && arguments.callee.apply( this, args.splice(1, args.length - 1 )) );
1567
+ };
1568
+
1569
+ return innerEquiv;
1570
+ }());
1571
+
1572
+ /**
1573
+ * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
1574
+ * http://flesler.blogspot.com Licensed under BSD
1575
+ * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
1576
+ *
1577
+ * @projectDescription Advanced and extensible data dumping for Javascript.
1578
+ * @version 1.0.0
1579
+ * @author Ariel Flesler
1580
+ * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
1581
+ */
1582
+ QUnit.jsDump = (function() {
1583
+ function quote( str ) {
1584
+ return '"' + str.toString().replace( /"/g, '\\"' ) + '"';
1585
+ }
1586
+ function literal( o ) {
1587
+ return o + "";
1588
+ }
1589
+ function join( pre, arr, post ) {
1590
+ var s = jsDump.separator(),
1591
+ base = jsDump.indent(),
1592
+ inner = jsDump.indent(1);
1593
+ if ( arr.join ) {
1594
+ arr = arr.join( "," + s + inner );
1595
+ }
1596
+ if ( !arr ) {
1597
+ return pre + post;
1598
+ }
1599
+ return [ pre, inner + arr, base + post ].join(s);
1600
+ }
1601
+ function array( arr, stack ) {
1602
+ var i = arr.length, ret = new Array(i);
1603
+ this.up();
1604
+ while ( i-- ) {
1605
+ ret[i] = this.parse( arr[i] , undefined , stack);
1606
+ }
1607
+ this.down();
1608
+ return join( "[", ret, "]" );
1609
+ }
1610
+
1611
+ var reName = /^function (\w+)/,
1612
+ jsDump = {
1613
+ parse: function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance
1614
+ stack = stack || [ ];
1615
+ var inStack, res,
1616
+ parser = this.parsers[ type || this.typeOf(obj) ];
1617
+
1618
+ type = typeof parser;
1619
+ inStack = inArray( obj, stack );
1620
+
1621
+ if ( inStack != -1 ) {
1622
+ return "recursion(" + (inStack - stack.length) + ")";
1623
+ }
1624
+ //else
1625
+ if ( type == "function" ) {
1626
+ stack.push( obj );
1627
+ res = parser.call( this, obj, stack );
1628
+ stack.pop();
1629
+ return res;
1630
+ }
1631
+ // else
1632
+ return ( type == "string" ) ? parser : this.parsers.error;
1633
+ },
1634
+ typeOf: function( obj ) {
1635
+ var type;
1636
+ if ( obj === null ) {
1637
+ type = "null";
1638
+ } else if ( typeof obj === "undefined" ) {
1639
+ type = "undefined";
1640
+ } else if ( QUnit.is( "regexp", obj) ) {
1641
+ type = "regexp";
1642
+ } else if ( QUnit.is( "date", obj) ) {
1643
+ type = "date";
1644
+ } else if ( QUnit.is( "function", obj) ) {
1645
+ type = "function";
1646
+ } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) {
1647
+ type = "window";
1648
+ } else if ( obj.nodeType === 9 ) {
1649
+ type = "document";
1650
+ } else if ( obj.nodeType ) {
1651
+ type = "node";
1652
+ } else if (
1653
+ // native arrays
1654
+ toString.call( obj ) === "[object Array]" ||
1655
+ // NodeList objects
1656
+ ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )
1657
+ ) {
1658
+ type = "array";
1659
+ } else {
1660
+ type = typeof obj;
1661
+ }
1662
+ return type;
1663
+ },
1664
+ separator: function() {
1665
+ return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&nbsp;" : " ";
1666
+ },
1667
+ indent: function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
1668
+ if ( !this.multiline ) {
1669
+ return "";
1670
+ }
1671
+ var chr = this.indentChar;
1672
+ if ( this.HTML ) {
1673
+ chr = chr.replace( /\t/g, " " ).replace( / /g, "&nbsp;" );
1674
+ }
1675
+ return new Array( this._depth_ + (extra||0) ).join(chr);
1676
+ },
1677
+ up: function( a ) {
1678
+ this._depth_ += a || 1;
1679
+ },
1680
+ down: function( a ) {
1681
+ this._depth_ -= a || 1;
1682
+ },
1683
+ setParser: function( name, parser ) {
1684
+ this.parsers[name] = parser;
1685
+ },
1686
+ // The next 3 are exposed so you can use them
1687
+ quote: quote,
1688
+ literal: literal,
1689
+ join: join,
1690
+ //
1691
+ _depth_: 1,
1692
+ // This is the list of parsers, to modify them, use jsDump.setParser
1693
+ parsers: {
1694
+ window: "[Window]",
1695
+ document: "[Document]",
1696
+ error: "[ERROR]", //when no parser is found, shouldn"t happen
1697
+ unknown: "[Unknown]",
1698
+ "null": "null",
1699
+ "undefined": "undefined",
1700
+ "function": function( fn ) {
1701
+ var ret = "function",
1702
+ name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];//functions never have name in IE
1703
+
1704
+ if ( name ) {
1705
+ ret += " " + name;
1706
+ }
1707
+ ret += "( ";
1708
+
1709
+ ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" );
1710
+ return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" );
1711
+ },
1712
+ array: array,
1713
+ nodelist: array,
1714
+ "arguments": array,
1715
+ object: function( map, stack ) {
1716
+ var ret = [ ], keys, key, val, i;
1717
+ QUnit.jsDump.up();
1718
+ if ( Object.keys ) {
1719
+ keys = Object.keys( map );
1720
+ } else {
1721
+ keys = [];
1722
+ for ( key in map ) {
1723
+ keys.push( key );
1724
+ }
1725
+ }
1726
+ keys.sort();
1727
+ for ( i = 0; i < keys.length; i++ ) {
1728
+ key = keys[ i ];
1729
+ val = map[ key ];
1730
+ ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) );
1731
+ }
1732
+ QUnit.jsDump.down();
1733
+ return join( "{", ret, "}" );
1734
+ },
1735
+ node: function( node ) {
1736
+ var a, val,
1737
+ open = QUnit.jsDump.HTML ? "&lt;" : "<",
1738
+ close = QUnit.jsDump.HTML ? "&gt;" : ">",
1739
+ tag = node.nodeName.toLowerCase(),
1740
+ ret = open + tag;
1741
+
1742
+ for ( a in QUnit.jsDump.DOMAttrs ) {
1743
+ val = node[ QUnit.jsDump.DOMAttrs[a] ];
1744
+ if ( val ) {
1745
+ ret += " " + a + "=" + QUnit.jsDump.parse( val, "attribute" );
1746
+ }
1747
+ }
1748
+ return ret + close + open + "/" + tag + close;
1749
+ },
1750
+ functionArgs: function( fn ) {//function calls it internally, it's the arguments part of the function
1751
+ var args,
1752
+ l = fn.length;
1753
+
1754
+ if ( !l ) {
1755
+ return "";
1756
+ }
1757
+
1758
+ args = new Array(l);
1759
+ while ( l-- ) {
1760
+ args[l] = String.fromCharCode(97+l);//97 is 'a'
1761
+ }
1762
+ return " " + args.join( ", " ) + " ";
1763
+ },
1764
+ key: quote, //object calls it internally, the key part of an item in a map
1765
+ functionCode: "[code]", //function calls it internally, it's the content of the function
1766
+ attribute: quote, //node calls it internally, it's an html attribute value
1767
+ string: quote,
1768
+ date: quote,
1769
+ regexp: literal, //regex
1770
+ number: literal,
1771
+ "boolean": literal
1772
+ },
1773
+ DOMAttrs: {
1774
+ //attributes to dump from nodes, name=>realName
1775
+ id: "id",
1776
+ name: "name",
1777
+ "class": "className"
1778
+ },
1779
+ HTML: false,//if true, entities are escaped ( <, >, \t, space and \n )
1780
+ indentChar: " ",//indentation unit
1781
+ multiline: true //if true, items in a collection, are separated by a \n, else just a space.
1782
+ };
1783
+
1784
+ return jsDump;
1785
+ }());
1786
+
1787
+ // from Sizzle.js
1788
+ function getText( elems ) {
1789
+ var i, elem,
1790
+ ret = "";
1791
+
1792
+ for ( i = 0; elems[i]; i++ ) {
1793
+ elem = elems[i];
1794
+
1795
+ // Get the text from text nodes and CDATA nodes
1796
+ if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
1797
+ ret += elem.nodeValue;
1798
+
1799
+ // Traverse everything else, except comment nodes
1800
+ } else if ( elem.nodeType !== 8 ) {
1801
+ ret += getText( elem.childNodes );
1802
+ }
1803
+ }
1804
+
1805
+ return ret;
1806
+ }
1807
+
1808
+ // from jquery.js
1809
+ function inArray( elem, array ) {
1810
+ if ( array.indexOf ) {
1811
+ return array.indexOf( elem );
1812
+ }
1813
+
1814
+ for ( var i = 0, length = array.length; i < length; i++ ) {
1815
+ if ( array[ i ] === elem ) {
1816
+ return i;
1817
+ }
1818
+ }
1819
+
1820
+ return -1;
1821
+ }
1822
+
1823
+ /*
1824
+ * Javascript Diff Algorithm
1825
+ * By John Resig (http://ejohn.org/)
1826
+ * Modified by Chu Alan "sprite"
1827
+ *
1828
+ * Released under the MIT license.
1829
+ *
1830
+ * More Info:
1831
+ * http://ejohn.org/projects/javascript-diff-algorithm/
1832
+ *
1833
+ * Usage: QUnit.diff(expected, actual)
1834
+ *
1835
+ * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over"
1836
+ */
1837
+ QUnit.diff = (function() {
1838
+ function diff( o, n ) {
1839
+ var i,
1840
+ ns = {},
1841
+ os = {};
1842
+
1843
+ for ( i = 0; i < n.length; i++ ) {
1844
+ if ( ns[ n[i] ] == null ) {
1845
+ ns[ n[i] ] = {
1846
+ rows: [],
1847
+ o: null
1848
+ };
1849
+ }
1850
+ ns[ n[i] ].rows.push( i );
1851
+ }
1852
+
1853
+ for ( i = 0; i < o.length; i++ ) {
1854
+ if ( os[ o[i] ] == null ) {
1855
+ os[ o[i] ] = {
1856
+ rows: [],
1857
+ n: null
1858
+ };
1859
+ }
1860
+ os[ o[i] ].rows.push( i );
1861
+ }
1862
+
1863
+ for ( i in ns ) {
1864
+ if ( !hasOwn.call( ns, i ) ) {
1865
+ continue;
1866
+ }
1867
+ if ( ns[i].rows.length == 1 && typeof os[i] != "undefined" && os[i].rows.length == 1 ) {
1868
+ n[ ns[i].rows[0] ] = {
1869
+ text: n[ ns[i].rows[0] ],
1870
+ row: os[i].rows[0]
1871
+ };
1872
+ o[ os[i].rows[0] ] = {
1873
+ text: o[ os[i].rows[0] ],
1874
+ row: ns[i].rows[0]
1875
+ };
1876
+ }
1877
+ }
1878
+
1879
+ for ( i = 0; i < n.length - 1; i++ ) {
1880
+ if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&
1881
+ n[ i + 1 ] == o[ n[i].row + 1 ] ) {
1882
+
1883
+ n[ i + 1 ] = {
1884
+ text: n[ i + 1 ],
1885
+ row: n[i].row + 1
1886
+ };
1887
+ o[ n[i].row + 1 ] = {
1888
+ text: o[ n[i].row + 1 ],
1889
+ row: i + 1
1890
+ };
1891
+ }
1892
+ }
1893
+
1894
+ for ( i = n.length - 1; i > 0; i-- ) {
1895
+ if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&
1896
+ n[ i - 1 ] == o[ n[i].row - 1 ]) {
1897
+
1898
+ n[ i - 1 ] = {
1899
+ text: n[ i - 1 ],
1900
+ row: n[i].row - 1
1901
+ };
1902
+ o[ n[i].row - 1 ] = {
1903
+ text: o[ n[i].row - 1 ],
1904
+ row: i - 1
1905
+ };
1906
+ }
1907
+ }
1908
+
1909
+ return {
1910
+ o: o,
1911
+ n: n
1912
+ };
1913
+ }
1914
+
1915
+ return function( o, n ) {
1916
+ o = o.replace( /\s+$/, "" );
1917
+ n = n.replace( /\s+$/, "" );
1918
+
1919
+ var i, pre,
1920
+ str = "",
1921
+ out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ),
1922
+ oSpace = o.match(/\s+/g),
1923
+ nSpace = n.match(/\s+/g);
1924
+
1925
+ if ( oSpace == null ) {
1926
+ oSpace = [ " " ];
1927
+ }
1928
+ else {
1929
+ oSpace.push( " " );
1930
+ }
1931
+
1932
+ if ( nSpace == null ) {
1933
+ nSpace = [ " " ];
1934
+ }
1935
+ else {
1936
+ nSpace.push( " " );
1937
+ }
1938
+
1939
+ if ( out.n.length === 0 ) {
1940
+ for ( i = 0; i < out.o.length; i++ ) {
1941
+ str += "<del>" + out.o[i] + oSpace[i] + "</del>";
1942
+ }
1943
+ }
1944
+ else {
1945
+ if ( out.n[0].text == null ) {
1946
+ for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) {
1947
+ str += "<del>" + out.o[n] + oSpace[n] + "</del>";
1948
+ }
1949
+ }
1950
+
1951
+ for ( i = 0; i < out.n.length; i++ ) {
1952
+ if (out.n[i].text == null) {
1953
+ str += "<ins>" + out.n[i] + nSpace[i] + "</ins>";
1954
+ }
1955
+ else {
1956
+ // `pre` initialized at top of scope
1957
+ pre = "";
1958
+
1959
+ for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {
1960
+ pre += "<del>" + out.o[n] + oSpace[n] + "</del>";
1961
+ }
1962
+ str += " " + out.n[i].text + nSpace[i] + pre;
1963
+ }
1964
+ }
1965
+ }
1966
+
1967
+ return str;
1968
+ };
1969
+ }());
1970
+
1971
+ // for CommonJS enviroments, export everything
1972
+ if ( typeof exports !== "undefined" ) {
1973
+ extend(exports, QUnit);
1974
+ }
1975
+
1976
+ // get at whatever the global object is, like window in browsers
1977
+ }( (function() {return this;}.call()) ));