jquery-rails-multipart-xhr 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/.gitignore +6 -0
  2. data/Gemfile +4 -0
  3. data/README.md +63 -0
  4. data/Rakefile +1 -0
  5. data/jquery-rails-multipart-xhr.gemspec +26 -0
  6. data/lib/generators/jquery/multipart_xhr/install/install_generator.rb +20 -0
  7. data/lib/jquery-rails-multipart-xhr.rb +2 -0
  8. data/lib/jquery/rails/multipart_xhr.rb +13 -0
  9. data/lib/jquery/rails/multipart_xhr/engine.rb +10 -0
  10. data/lib/jquery/rails/multipart_xhr/railtie.rb +18 -0
  11. data/lib/jquery/rails/multipart_xhr/version.rb +8 -0
  12. data/test/.rbenv-gemsets +1 -0
  13. data/test/index.html +61 -0
  14. data/test/libs/jasmine-html.js +616 -0
  15. data/test/libs/jasmine-jquery.js +322 -0
  16. data/test/libs/jasmine.css +81 -0
  17. data/test/libs/jasmine.js +2530 -0
  18. data/test/libs/jquery-1.7.2.js +9404 -0
  19. data/test/libs/rails.js +384 -0
  20. data/test/rails_ext_spec.js +34 -0
  21. data/test/sendfile.html +0 -0
  22. data/test/spec_helper.js +28 -0
  23. data/test/test_app/Gemfile +9 -0
  24. data/test/test_app/README.rdoc +261 -0
  25. data/test/test_app/Rakefile +7 -0
  26. data/test/test_app/app/assets/images/rails.png +0 -0
  27. data/test/test_app/app/assets/javascripts/application.js +3 -0
  28. data/test/test_app/app/assets/stylesheets/application.css +13 -0
  29. data/test/test_app/app/controllers/application_controller.rb +3 -0
  30. data/test/test_app/app/controllers/index_controller.rb +10 -0
  31. data/test/test_app/app/helpers/application_helper.rb +2 -0
  32. data/test/test_app/app/helpers/index_helper.rb +2 -0
  33. data/test/test_app/app/views/index/index.html.erb +5 -0
  34. data/test/test_app/app/views/index/upload.js.erb +0 -0
  35. data/test/test_app/app/views/layouts/application.html.erb +14 -0
  36. data/test/test_app/config.ru +4 -0
  37. data/test/test_app/config/application.rb +68 -0
  38. data/test/test_app/config/boot.rb +6 -0
  39. data/test/test_app/config/environment.rb +5 -0
  40. data/test/test_app/config/environments/development.rb +31 -0
  41. data/test/test_app/config/environments/production.rb +64 -0
  42. data/test/test_app/config/environments/test.rb +35 -0
  43. data/test/test_app/config/initializers/backtrace_silencers.rb +7 -0
  44. data/test/test_app/config/initializers/inflections.rb +15 -0
  45. data/test/test_app/config/initializers/mime_types.rb +5 -0
  46. data/test/test_app/config/initializers/secret_token.rb +7 -0
  47. data/test/test_app/config/initializers/session_store.rb +8 -0
  48. data/test/test_app/config/initializers/wrap_parameters.rb +10 -0
  49. data/test/test_app/config/locales/en.yml +5 -0
  50. data/test/test_app/config/routes.rb +4 -0
  51. data/test/test_app/db/seeds.rb +7 -0
  52. data/test/test_app/doc/README_FOR_APP +2 -0
  53. data/test/test_app/log/development.log +393 -0
  54. data/test/test_app/public/404.html +26 -0
  55. data/test/test_app/public/422.html +26 -0
  56. data/test/test_app/public/500.html +25 -0
  57. data/test/test_app/public/favicon.ico +0 -0
  58. data/test/test_app/public/robots.txt +5 -0
  59. data/test/test_app/script/rails +6 -0
  60. data/test/test_app/tmp/cache/assets/C8F/390/sprockets%2F6b7d5e919177af65c02182147de40988 +0 -0
  61. data/test/test_app/tmp/cache/assets/CD8/0A0/sprockets%2F5141580125473c05f47bff0c8bb8dd80 +0 -0
  62. data/test/test_app/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
  63. data/test/test_app/tmp/cache/assets/CF0/DA0/sprockets%2Fd7d5b37686831d37c4dd75e645f5e016 +0 -0
  64. data/test/test_app/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  65. data/test/test_app/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
  66. data/test/test_app/tmp/cache/assets/D53/0E0/sprockets%2F88f3d17e4cef7464b29ab1d468495af6 +0 -0
  67. data/test/test_app/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
  68. data/test/test_app/tmp/cache/assets/D6D/6E0/sprockets%2Ff0871e805cb1dd84c3a8a024f925e7af +0 -0
  69. data/test/test_app/tmp/cache/assets/D94/5C0/sprockets%2Fbfc22af1dbf2c597a27bfa0388020f76 +0 -0
  70. data/test/test_app/tmp/cache/assets/DAC/540/sprockets%2Fee54428bf456feb108fa5ac58fe768b3 +0 -0
  71. data/test/test_app/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
  72. data/test/test_app/tmp/cache/assets/DF1/800/sprockets%2Fb3fb6dd6651d2f1ecacf3c52b1f7e223 +0 -0
  73. data/test/test_app/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  74. data/test/test_app/tmp/cache/assets/E0A/940/sprockets%2Fff36bca4fabd6d0690d947ef06ff8d26 +0 -0
  75. data/test/test_app/tmp/cache/assets/E25/4C0/sprockets%2Fde2fd9fd11c04a582cdbbe3d84a35ae6 +0 -0
  76. data/test/xhr_spec.js +14 -0
  77. data/vendor/assets/javascripts/jquery_ujs_multipart_xhr.js +172 -0
  78. data/vendor/assets/javascripts/jquery_ujs_xhr.js +1 -0
  79. metadata +254 -0
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .DS_Store
6
+ test/test_app/vendor/bundle
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in jquery-rails-multipartxhr.gemspec
4
+ gemspec
@@ -0,0 +1,63 @@
1
+ # Jquery-rails Multipart XHR
2
+
3
+ Rails' default jQuery-ujs provides means for handling AJAX requests in which a remote form contains file inputs with values. Adding this adapter expands on that functionality to add support for native XHR uploads when supported by the browser.
4
+
5
+ ## Rails 3.1
6
+
7
+ The gem is configured to work out of the box with the asset pipeline. In `apps/assets/javascripts/application.js` add the following
8
+
9
+ //= require jquery
10
+ //= require jquery_ujs
11
+ //= require jquery_ujs_multipart_xhr
12
+
13
+ Make sure to include jquery and jquery_ujs first, as this library extends the default ujs functionality to support file uploads.
14
+
15
+ ## Rails 3.0
16
+
17
+ Generators are included for copying files to public/javascripts. Either download the `jquery_ujs_multipart_xhr.js` file and add it to your project, or add the gem to your Gemfile and run the generator.
18
+
19
+ gem "jquery-rails-multipart-xhr"
20
+
21
+ ## Usage
22
+
23
+ As with the official ujs library, multipart-xhr is unobtrusive in how it functions. When included, forms are automatically updated to support the additional functionality, provided the browser supports it. Currently the following supports XHR uploading natively:
24
+
25
+ * Safari / Chrome (latest)
26
+ * Firefox 3.6+
27
+
28
+ To utilize progress events in your form, bind the `progress` event.
29
+
30
+ $('form').bind('progress', function( event ){ ... your code here. });
31
+
32
+ The progress event is populated with 3 values for tracking submission progress:
33
+
34
+ `loaded`: The bytes currently completed
35
+
36
+ `total`: The total number of bytes in the request. ( note, this is a approximate (though fairly accurate) number provided by XHRHttpRequest, see below for getting more accurate totals )
37
+
38
+ `percent`: An integer representing the percent complete ( for convenience really )
39
+
40
+ Because the `total` provided by XHRHttpRequest isn't always 100% accurate, an additional event `ajax:upload:start` is provided which passes the total bytes of all attached files from their native File objects.
41
+
42
+ $('form').bind('ajax:upload:start', function( event, bytes ){
43
+ // bytes = a sum of all of the attached files, populated from the
44
+ // input.files[x].fileSize property.
45
+ });
46
+
47
+
48
+ ## Supporting IE?
49
+
50
+ In the future we've considered adding support for a flash-based fallback for our friend Internet Explorer ( at least until they release version 19 and finally catch up with the rest of the world (maybe) ) though I'd be interested to know any thoughts people may have on that. Ideally this would include Rack middleware to handle processing files prior to form submission, and then "re-attach" those files back to the resulting form submission. Suggestions are very welcome!
51
+
52
+ ## Contributing
53
+
54
+ File uploading still sucks, mostly due to shoddy browser support (cough... IE), so any suggestions, pull request, ideas, etc to help enhance the user experience are very welcome. As are bug reports etc of course!
55
+ Brownie points if you have suggestions for testing the submit/event functionality in Jasmine :) Since we're relying on a filled in file input, its kind of a problem.
56
+
57
+ ### Copyright
58
+
59
+ Copyright (c) 2011 Brent Kirby / Kurb Media LLC. Licensed under the MIT license.
60
+ Do whatever you want with it, just give me a shout if you do something cool k? :)
61
+
62
+
63
+
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require File.expand_path('../lib/jquery/rails/multipart_xhr/version', __FILE__)
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "jquery-rails-multipart-xhr"
7
+ s.version = Jquery::Rails::MultipartXHR::VERSION
8
+ s.authors = ["Brent Kirby"]
9
+ s.email = ["brent@kurbmedia.com"]
10
+ s.homepage = "https://github.com/kurbmedia/jquery-rails-multipart-xhr"
11
+ s.summary = %q{Adds additional support for XHR file uploads to jquery_ujs}
12
+ s.description = %q{Rails' default jQuery-ujs provides means for handling AJAX requests in which a remote form contains file inputs with values. Adding this adapter expands on that functionality to add support for native XHR uploads when supported by the browser.}
13
+
14
+ s.required_rubygems_version = ">= 1.3.6"
15
+ s.rubyforge_project = "jquery-rails-multipart-xhr"
16
+ s.add_dependency "railties", "~> 3.0"
17
+ s.add_dependency "thor", "~> 0.14"
18
+ s.add_development_dependency "bundler", "~> 1.0.0"
19
+ s.add_development_dependency "rails", "~> 3.0"
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
+ s.require_paths = ["lib"]
25
+
26
+ end
@@ -0,0 +1,20 @@
1
+ require 'rails'
2
+
3
+ module Jquery
4
+ module MultipartXHR
5
+
6
+ module Generators
7
+ class InstallGenerator < ::Rails::Generators::Base
8
+
9
+ desc "This generator installs the jQuery-rails Multipart XHR files"
10
+ source_root File.expand_path('../../../../../../vendor/assets/javascripts', __FILE__)
11
+
12
+ def copy_multipart_xhr_driver
13
+ say_status("copying", "jQuery UJS Multipart XHR adapter (#{Jquery::Rails::JQUERY_MULTIPART_XHR_VERSION})", :green)
14
+ copy_file "jquery_ujs_multipart_xhr.js", "public/javascripts/jquery_ujs_multipart_xhr.js"
15
+ end
16
+
17
+ end
18
+ end
19
+ end
20
+ end if ::Rails.version < "3.1"
@@ -0,0 +1,2 @@
1
+ require "jquery/rails/multipart_xhr"
2
+
@@ -0,0 +1,13 @@
1
+ module Jquery
2
+ module Rails
3
+ module MultipartXHR
4
+ if ::Rails.version < "3.1"
5
+ require 'jquery/rails/multipart_xhr/railtie'
6
+ else
7
+ require 'jquery/rails/multipart_xhr/engine'
8
+ end
9
+ require 'jquery/rails/multipart_xhr/version'
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ module Jquery
2
+ module Rails
3
+
4
+ module MultipartXHR
5
+ class Engine < ::Rails::Engine
6
+ end
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,18 @@
1
+ begin
2
+ require 'jquery/rails/railtie'
3
+ rescue LoadError
4
+ end
5
+
6
+ module Jquery
7
+ module Rails
8
+
9
+ module MultipartXHR
10
+ class Railtie < ::Rails::Railtie
11
+ config.before_configuration do
12
+ config.action_view.javascript_expansions[:defaults] << 'jquery_ujs_multipart_xhr'
13
+ end
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,8 @@
1
+ module Jquery
2
+ module Rails
3
+ JQUERY_MULTIPART_XHR_VERSION = "0.0.2"
4
+ module MultipartXHR
5
+ VERSION = "0.0.2"
6
+ end
7
+ end
8
+ end
@@ -0,0 +1 @@
1
+ global
@@ -0,0 +1,61 @@
1
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
2
+ "http://www.w3.org/TR/html4/loose.dtd">
3
+ <html>
4
+ <head>
5
+ <title>Jasmine Spec Runner</title>
6
+ <link rel="stylesheet" type="text/css" href="libs/jasmine.css">
7
+ <script src="libs/jquery-1.7.2.js" type="text/javascript" charset="utf-8"></script>
8
+ <script type="text/javascript" src="libs/jasmine.js"></script>
9
+ <script type="text/javascript" src="libs/jasmine-html.js"></script>
10
+ <script type="text/javascript" src="libs/jasmine-jquery.js"></script>
11
+ <script type="text/javascript" src="libs/rails.js"></script>
12
+ <script type="text/javascript" src="../vendor/assets/javascripts/jquery_ujs_multipart_xhr.js"></script>
13
+ <!-- include spec files here... -->
14
+ <script type="text/javascript" src="spec_helper.js"></script>
15
+ <script type="text/javascript" src="xhr_spec.js"></script>
16
+ <script type="text/javascript" src="rails_ext_spec.js"></script>
17
+ <script type="text/javascript" charset="utf-8">
18
+ //<![CDATA[
19
+ (function() {
20
+ var jasmineEnv = jasmine.getEnv();
21
+ jasmineEnv.updateInterval = 1000;
22
+
23
+ var htmlReporter = new jasmine.HtmlReporter();
24
+ jasmineEnv.addReporter(htmlReporter);
25
+ jasmineEnv.specFilter = function(spec) {
26
+ return htmlReporter.specFilter(spec);
27
+ };
28
+
29
+ var currentWindowOnload = window.onload;
30
+
31
+ window.onload = function() {
32
+ if (currentWindowOnload) {
33
+ currentWindowOnload();
34
+ }
35
+ execJasmine();
36
+ };
37
+
38
+ function execJasmine() {
39
+ jasmineEnv.execute();
40
+ }
41
+
42
+ })();
43
+ //]]>
44
+ </script>
45
+ </head>
46
+
47
+ <body>
48
+ <form id="upload_form" method="post" data-remote="true" action="sendfile.html">
49
+ <input name="text1" value="" />
50
+ <input type="hidden" name="_method" value="post" />
51
+ <input type="file" name="testfile" />
52
+ <input type="submit" name="submit" value="Submit" />
53
+ </form>
54
+ <form id="upload_form2" method="post" data-remote="true" action="sendfile.html">
55
+ <input name="text2" value="" />
56
+ <input type="file" name="testfile2" />
57
+ <input type="hidden" name="_method" value="post" />
58
+ <input type="submit" name="submit" value="Submit" />
59
+ </form>
60
+ </body>
61
+ </html>
@@ -0,0 +1,616 @@
1
+ jasmine.HtmlReporterHelpers = {};
2
+
3
+ jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
4
+ var el = document.createElement(type);
5
+
6
+ for (var i = 2; i < arguments.length; i++) {
7
+ var child = arguments[i];
8
+
9
+ if (typeof child === 'string') {
10
+ el.appendChild(document.createTextNode(child));
11
+ } else {
12
+ if (child) {
13
+ el.appendChild(child);
14
+ }
15
+ }
16
+ }
17
+
18
+ for (var attr in attrs) {
19
+ if (attr == "className") {
20
+ el[attr] = attrs[attr];
21
+ } else {
22
+ el.setAttribute(attr, attrs[attr]);
23
+ }
24
+ }
25
+
26
+ return el;
27
+ };
28
+
29
+ jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
30
+ var results = child.results();
31
+ var status = results.passed() ? 'passed' : 'failed';
32
+ if (results.skipped) {
33
+ status = 'skipped';
34
+ }
35
+
36
+ return status;
37
+ };
38
+
39
+ jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
40
+ var parentDiv = this.dom.summary;
41
+ var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
42
+ var parent = child[parentSuite];
43
+
44
+ if (parent) {
45
+ if (typeof this.views.suites[parent.id] == 'undefined') {
46
+ this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
47
+ }
48
+ parentDiv = this.views.suites[parent.id].element;
49
+ }
50
+
51
+ parentDiv.appendChild(childElement);
52
+ };
53
+
54
+
55
+ jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
56
+ for(var fn in jasmine.HtmlReporterHelpers) {
57
+ ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
58
+ }
59
+ };
60
+
61
+ jasmine.HtmlReporter = function(_doc) {
62
+ var self = this;
63
+ var doc = _doc || window.document;
64
+
65
+ var reporterView;
66
+
67
+ var dom = {};
68
+
69
+ // Jasmine Reporter Public Interface
70
+ self.logRunningSpecs = false;
71
+
72
+ self.reportRunnerStarting = function(runner) {
73
+ var specs = runner.specs() || [];
74
+
75
+ if (specs.length == 0) {
76
+ return;
77
+ }
78
+
79
+ createReporterDom(runner.env.versionString());
80
+ doc.body.appendChild(dom.reporter);
81
+
82
+ reporterView = new jasmine.HtmlReporter.ReporterView(dom);
83
+ reporterView.addSpecs(specs, self.specFilter);
84
+ };
85
+
86
+ self.reportRunnerResults = function(runner) {
87
+ reporterView && reporterView.complete();
88
+ };
89
+
90
+ self.reportSuiteResults = function(suite) {
91
+ reporterView.suiteComplete(suite);
92
+ };
93
+
94
+ self.reportSpecStarting = function(spec) {
95
+ if (self.logRunningSpecs) {
96
+ self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
97
+ }
98
+ };
99
+
100
+ self.reportSpecResults = function(spec) {
101
+ reporterView.specComplete(spec);
102
+ };
103
+
104
+ self.log = function() {
105
+ var console = jasmine.getGlobal().console;
106
+ if (console && console.log) {
107
+ if (console.log.apply) {
108
+ console.log.apply(console, arguments);
109
+ } else {
110
+ console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
111
+ }
112
+ }
113
+ };
114
+
115
+ self.specFilter = function(spec) {
116
+ if (!focusedSpecName()) {
117
+ return true;
118
+ }
119
+
120
+ return spec.getFullName().indexOf(focusedSpecName()) === 0;
121
+ };
122
+
123
+ return self;
124
+
125
+ function focusedSpecName() {
126
+ var specName;
127
+
128
+ (function memoizeFocusedSpec() {
129
+ if (specName) {
130
+ return;
131
+ }
132
+
133
+ var paramMap = [];
134
+ var params = doc.location.search.substring(1).split('&');
135
+
136
+ for (var i = 0; i < params.length; i++) {
137
+ var p = params[i].split('=');
138
+ paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
139
+ }
140
+
141
+ specName = paramMap.spec;
142
+ })();
143
+
144
+ return specName;
145
+ }
146
+
147
+ function createReporterDom(version) {
148
+ dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
149
+ dom.banner = self.createDom('div', { className: 'banner' },
150
+ self.createDom('span', { className: 'title' }, "Jasmine "),
151
+ self.createDom('span', { className: 'version' }, version)),
152
+
153
+ dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
154
+ dom.alert = self.createDom('div', {className: 'alert'}),
155
+ dom.results = self.createDom('div', {className: 'results'},
156
+ dom.summary = self.createDom('div', { className: 'summary' }),
157
+ dom.details = self.createDom('div', { id: 'details' }))
158
+ );
159
+ }
160
+ };
161
+ jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);jasmine.HtmlReporter.ReporterView = function(dom) {
162
+ this.startedAt = new Date();
163
+ this.runningSpecCount = 0;
164
+ this.completeSpecCount = 0;
165
+ this.passedCount = 0;
166
+ this.failedCount = 0;
167
+ this.skippedCount = 0;
168
+
169
+ this.createResultsMenu = function() {
170
+ this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
171
+ this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
172
+ ' | ',
173
+ this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
174
+
175
+ this.summaryMenuItem.onclick = function() {
176
+ dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
177
+ };
178
+
179
+ this.detailsMenuItem.onclick = function() {
180
+ showDetails();
181
+ };
182
+ };
183
+
184
+ this.addSpecs = function(specs, specFilter) {
185
+ this.totalSpecCount = specs.length;
186
+
187
+ this.views = {
188
+ specs: {},
189
+ suites: {}
190
+ };
191
+
192
+ for (var i = 0; i < specs.length; i++) {
193
+ var spec = specs[i];
194
+ this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
195
+ if (specFilter(spec)) {
196
+ this.runningSpecCount++;
197
+ }
198
+ }
199
+ };
200
+
201
+ this.specComplete = function(spec) {
202
+ this.completeSpecCount++;
203
+
204
+ if (isUndefined(this.views.specs[spec.id])) {
205
+ this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
206
+ }
207
+
208
+ var specView = this.views.specs[spec.id];
209
+
210
+ switch (specView.status()) {
211
+ case 'passed':
212
+ this.passedCount++;
213
+ break;
214
+
215
+ case 'failed':
216
+ this.failedCount++;
217
+ break;
218
+
219
+ case 'skipped':
220
+ this.skippedCount++;
221
+ break;
222
+ }
223
+
224
+ specView.refresh();
225
+ this.refresh();
226
+ };
227
+
228
+ this.suiteComplete = function(suite) {
229
+ var suiteView = this.views.suites[suite.id];
230
+ if (isUndefined(suiteView)) {
231
+ return;
232
+ }
233
+ suiteView.refresh();
234
+ };
235
+
236
+ this.refresh = function() {
237
+
238
+ if (isUndefined(this.resultsMenu)) {
239
+ this.createResultsMenu();
240
+ }
241
+
242
+ // currently running UI
243
+ if (isUndefined(this.runningAlert)) {
244
+ this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"});
245
+ dom.alert.appendChild(this.runningAlert);
246
+ }
247
+ this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
248
+
249
+ // skipped specs UI
250
+ if (isUndefined(this.skippedAlert)) {
251
+ this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"});
252
+ }
253
+
254
+ this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
255
+
256
+ if (this.skippedCount === 1 && isDefined(dom.alert)) {
257
+ dom.alert.appendChild(this.skippedAlert);
258
+ }
259
+
260
+ // passing specs UI
261
+ if (isUndefined(this.passedAlert)) {
262
+ this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"});
263
+ }
264
+ this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
265
+
266
+ // failing specs UI
267
+ if (isUndefined(this.failedAlert)) {
268
+ this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
269
+ }
270
+ this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
271
+
272
+ if (this.failedCount === 1 && isDefined(dom.alert)) {
273
+ dom.alert.appendChild(this.failedAlert);
274
+ dom.alert.appendChild(this.resultsMenu);
275
+ }
276
+
277
+ // summary info
278
+ this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
279
+ this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
280
+ };
281
+
282
+ this.complete = function() {
283
+ dom.alert.removeChild(this.runningAlert);
284
+
285
+ this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
286
+
287
+ if (this.failedCount === 0) {
288
+ dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
289
+ } else {
290
+ showDetails();
291
+ }
292
+
293
+ dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
294
+ };
295
+
296
+ return this;
297
+
298
+ function showDetails() {
299
+ if (dom.reporter.className.search(/showDetails/) === -1) {
300
+ dom.reporter.className += " showDetails";
301
+ }
302
+ }
303
+
304
+ function isUndefined(obj) {
305
+ return typeof obj === 'undefined';
306
+ }
307
+
308
+ function isDefined(obj) {
309
+ return !isUndefined(obj);
310
+ }
311
+
312
+ function specPluralizedFor(count) {
313
+ var str = count + " spec";
314
+ if (count > 1) {
315
+ str += "s"
316
+ }
317
+ return str;
318
+ }
319
+
320
+ };
321
+
322
+ jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
323
+
324
+
325
+ jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
326
+ this.spec = spec;
327
+ this.dom = dom;
328
+ this.views = views;
329
+
330
+ this.symbol = this.createDom('li', { className: 'pending' });
331
+ this.dom.symbolSummary.appendChild(this.symbol);
332
+
333
+ this.summary = this.createDom('div', { className: 'specSummary' },
334
+ this.createDom('a', {
335
+ className: 'description',
336
+ href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
337
+ title: this.spec.getFullName()
338
+ }, this.spec.description)
339
+ );
340
+
341
+ this.detail = this.createDom('div', { className: 'specDetail' },
342
+ this.createDom('a', {
343
+ className: 'description',
344
+ href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
345
+ title: this.spec.getFullName()
346
+ }, this.spec.getFullName())
347
+ );
348
+ };
349
+
350
+ jasmine.HtmlReporter.SpecView.prototype.status = function() {
351
+ return this.getSpecStatus(this.spec);
352
+ };
353
+
354
+ jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
355
+ this.symbol.className = this.status();
356
+
357
+ switch (this.status()) {
358
+ case 'skipped':
359
+ break;
360
+
361
+ case 'passed':
362
+ this.appendSummaryToSuiteDiv();
363
+ break;
364
+
365
+ case 'failed':
366
+ this.appendSummaryToSuiteDiv();
367
+ this.appendFailureDetail();
368
+ break;
369
+ }
370
+ };
371
+
372
+ jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
373
+ this.summary.className += ' ' + this.status();
374
+ this.appendToSummary(this.spec, this.summary);
375
+ };
376
+
377
+ jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
378
+ this.detail.className += ' ' + this.status();
379
+
380
+ var resultItems = this.spec.results().getItems();
381
+ var messagesDiv = this.createDom('div', { className: 'messages' });
382
+
383
+ for (var i = 0; i < resultItems.length; i++) {
384
+ var result = resultItems[i];
385
+
386
+ if (result.type == 'log') {
387
+ messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
388
+ } else if (result.type == 'expect' && result.passed && !result.passed()) {
389
+ messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
390
+
391
+ if (result.trace.stack) {
392
+ messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
393
+ }
394
+ }
395
+ }
396
+
397
+ if (messagesDiv.childNodes.length > 0) {
398
+ this.detail.appendChild(messagesDiv);
399
+ this.dom.details.appendChild(this.detail);
400
+ }
401
+ };
402
+
403
+ jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
404
+ this.suite = suite;
405
+ this.dom = dom;
406
+ this.views = views;
407
+
408
+ this.element = this.createDom('div', { className: 'suite' },
409
+ this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description)
410
+ );
411
+
412
+ this.appendToSummary(this.suite, this.element);
413
+ };
414
+
415
+ jasmine.HtmlReporter.SuiteView.prototype.status = function() {
416
+ return this.getSpecStatus(this.suite);
417
+ };
418
+
419
+ jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
420
+ this.element.className += " " + this.status();
421
+ };
422
+
423
+ jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
424
+
425
+ /* @deprecated Use jasmine.HtmlReporter instead
426
+ */
427
+ jasmine.TrivialReporter = function(doc) {
428
+ this.document = doc || document;
429
+ this.suiteDivs = {};
430
+ this.logRunningSpecs = false;
431
+ };
432
+
433
+ jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
434
+ var el = document.createElement(type);
435
+
436
+ for (var i = 2; i < arguments.length; i++) {
437
+ var child = arguments[i];
438
+
439
+ if (typeof child === 'string') {
440
+ el.appendChild(document.createTextNode(child));
441
+ } else {
442
+ if (child) { el.appendChild(child); }
443
+ }
444
+ }
445
+
446
+ for (var attr in attrs) {
447
+ if (attr == "className") {
448
+ el[attr] = attrs[attr];
449
+ } else {
450
+ el.setAttribute(attr, attrs[attr]);
451
+ }
452
+ }
453
+
454
+ return el;
455
+ };
456
+
457
+ jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
458
+ var showPassed, showSkipped;
459
+
460
+ this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
461
+ this.createDom('div', { className: 'banner' },
462
+ this.createDom('div', { className: 'logo' },
463
+ this.createDom('span', { className: 'title' }, "Jasmine"),
464
+ this.createDom('span', { className: 'version' }, runner.env.versionString())),
465
+ this.createDom('div', { className: 'options' },
466
+ "Show ",
467
+ showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
468
+ this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
469
+ showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
470
+ this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
471
+ )
472
+ ),
473
+
474
+ this.runnerDiv = this.createDom('div', { className: 'runner running' },
475
+ this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
476
+ this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
477
+ this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
478
+ );
479
+
480
+ this.document.body.appendChild(this.outerDiv);
481
+
482
+ var suites = runner.suites();
483
+ for (var i = 0; i < suites.length; i++) {
484
+ var suite = suites[i];
485
+ var suiteDiv = this.createDom('div', { className: 'suite' },
486
+ this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
487
+ this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
488
+ this.suiteDivs[suite.id] = suiteDiv;
489
+ var parentDiv = this.outerDiv;
490
+ if (suite.parentSuite) {
491
+ parentDiv = this.suiteDivs[suite.parentSuite.id];
492
+ }
493
+ parentDiv.appendChild(suiteDiv);
494
+ }
495
+
496
+ this.startedAt = new Date();
497
+
498
+ var self = this;
499
+ showPassed.onclick = function(evt) {
500
+ if (showPassed.checked) {
501
+ self.outerDiv.className += ' show-passed';
502
+ } else {
503
+ self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
504
+ }
505
+ };
506
+
507
+ showSkipped.onclick = function(evt) {
508
+ if (showSkipped.checked) {
509
+ self.outerDiv.className += ' show-skipped';
510
+ } else {
511
+ self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
512
+ }
513
+ };
514
+ };
515
+
516
+ jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
517
+ var results = runner.results();
518
+ var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
519
+ this.runnerDiv.setAttribute("class", className);
520
+ //do it twice for IE
521
+ this.runnerDiv.setAttribute("className", className);
522
+ var specs = runner.specs();
523
+ var specCount = 0;
524
+ for (var i = 0; i < specs.length; i++) {
525
+ if (this.specFilter(specs[i])) {
526
+ specCount++;
527
+ }
528
+ }
529
+ var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
530
+ message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
531
+ this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
532
+
533
+ this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
534
+ };
535
+
536
+ jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
537
+ var results = suite.results();
538
+ var status = results.passed() ? 'passed' : 'failed';
539
+ if (results.totalCount === 0) { // todo: change this to check results.skipped
540
+ status = 'skipped';
541
+ }
542
+ this.suiteDivs[suite.id].className += " " + status;
543
+ };
544
+
545
+ jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
546
+ if (this.logRunningSpecs) {
547
+ this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
548
+ }
549
+ };
550
+
551
+ jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
552
+ var results = spec.results();
553
+ var status = results.passed() ? 'passed' : 'failed';
554
+ if (results.skipped) {
555
+ status = 'skipped';
556
+ }
557
+ var specDiv = this.createDom('div', { className: 'spec ' + status },
558
+ this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
559
+ this.createDom('a', {
560
+ className: 'description',
561
+ href: '?spec=' + encodeURIComponent(spec.getFullName()),
562
+ title: spec.getFullName()
563
+ }, spec.description));
564
+
565
+
566
+ var resultItems = results.getItems();
567
+ var messagesDiv = this.createDom('div', { className: 'messages' });
568
+ for (var i = 0; i < resultItems.length; i++) {
569
+ var result = resultItems[i];
570
+
571
+ if (result.type == 'log') {
572
+ messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
573
+ } else if (result.type == 'expect' && result.passed && !result.passed()) {
574
+ messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
575
+
576
+ if (result.trace.stack) {
577
+ messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
578
+ }
579
+ }
580
+ }
581
+
582
+ if (messagesDiv.childNodes.length > 0) {
583
+ specDiv.appendChild(messagesDiv);
584
+ }
585
+
586
+ this.suiteDivs[spec.suite.id].appendChild(specDiv);
587
+ };
588
+
589
+ jasmine.TrivialReporter.prototype.log = function() {
590
+ var console = jasmine.getGlobal().console;
591
+ if (console && console.log) {
592
+ if (console.log.apply) {
593
+ console.log.apply(console, arguments);
594
+ } else {
595
+ console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
596
+ }
597
+ }
598
+ };
599
+
600
+ jasmine.TrivialReporter.prototype.getLocation = function() {
601
+ return this.document.location;
602
+ };
603
+
604
+ jasmine.TrivialReporter.prototype.specFilter = function(spec) {
605
+ var paramMap = {};
606
+ var params = this.getLocation().search.substring(1).split('&');
607
+ for (var i = 0; i < params.length; i++) {
608
+ var p = params[i].split('=');
609
+ paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
610
+ }
611
+
612
+ if (!paramMap.spec) {
613
+ return true;
614
+ }
615
+ return spec.getFullName().indexOf(paramMap.spec) === 0;
616
+ };