remotipart 0.4.2 → 1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/History.rdoc CHANGED
@@ -1,6 +1,13 @@
1
1
  = History
2
2
 
3
- === 0.4.2 / 2011-26-08
3
+ === 1.0 / 2011-08-26
4
+
5
+ * New Features
6
+ * New dependency on simpler jquery.iframe-transport.js (no more form.js)
7
+ * New Rack middleware, making `remotipart_response` block obsolete in js.erb responses
8
+ * Better support for all requested ajax data-types
9
+
10
+ === 0.4.2 / 2011-08-26
4
11
 
5
12
  * Minor Enhancements
6
13
  * Updated to jquery.form.js v2.84
data/README.rdoc CHANGED
@@ -1,9 +1,10 @@
1
1
  = Remotipart
2
2
 
3
- Remotipart is a Ruby on Rails gem enabling remote multipart forms (AJAX style file uploads) with jQuery.
4
- This gem augments the native Rails jQuery remote form function enabling asynchronous file uploads with little to no modification to your application.
3
+ Remotipart is a Ruby on Rails gem enabling AJAX file uploads with jQuery in Rails 3.0 and Rails 3.1 remote forms.
4
+ This gem augments the native Rails jQuery remote form functionality enabling asynchronous file uploads with little to no modification to your application.
5
5
 
6
- {View Homepage and Demos}[http://www.alfajango.com/blog/remotipart-rails-gem/]
6
+ * {Homepage and Demos}[http://www.alfajango.com/blog/remotipart-rails-gem/]
7
+ * {How AJAX File Uploads Work}[http://www.alfajango.com/blog/ajax-file-uploads-with-the-iframe-method/]
7
8
 
8
9
  == Dependencies
9
10
 
@@ -12,24 +13,33 @@ This gem augments the native Rails jQuery remote form function enabling asynchro
12
13
 
13
14
  == Installation
14
15
 
15
- <b>If you're using an old version of the jquery-rails gem,
16
- make sure you have a supported jquery-ujs (rails.js or jquery_ujs.js) version from VERSION_COMPATIBILITY.</b>
16
+ <b>Your app should be using jquery-rails gem v1.0.12 or above.</b>
17
+
18
+ If you're using an old version of the jquery-rails gem,
19
+ make sure you have a supported jquery-ujs (rails.js or jquery_ujs.js) version from {VERSION_COMPATIBILITY}[https://github.com/JangoSteve/remotipart/blob/master/VERSION_COMPATIBILITY.rdoc].
17
20
 
18
21
  [1.]
19
22
  Install the Remotipart gem
20
23
 
21
- * Add this line to your GEMFILE (add the correct version from previous section if needed)
24
+ * Add this line to your GEMFILE (use the appropriate version from the compatibilty chart if needed)
22
25
 
23
- gem 'remotipart', '~> 0.4'
26
+ gem 'remotipart', '~> 1.0'
24
27
 
25
28
  * And run
26
29
 
27
30
  bundle install
28
31
 
32
+ === Rails 3.1
33
+
34
+ [2.]
35
+ The necessary js files will automatically be added to the asset pipeline, so add the following to app/assets/javascripts/application.js (right after <tt>//= require jquery_ujs</tt>):
36
+
37
+ //= require jquery.remotipart
38
+
29
39
  === Rails 3.0
30
40
 
31
41
  [2.]
32
- Run the Remotipart install generator to add jquery.form.js and jquery.remotipart.js to public/javascripts/
42
+ Run the Remotipart install generator to add jquery.iframe-transport.js and jquery.remotipart.js to public/javascripts/
33
43
 
34
44
  rails g remotipart:install
35
45
 
@@ -38,28 +48,12 @@ make sure you have a supported jquery-ujs (rails.js or jquery_ujs.js) version fr
38
48
 
39
49
  <%= javascript_include_tag :defaults %>
40
50
 
41
- === Rails 3.1
42
-
43
- [2.]
44
- The necessary js files will automatically be added to the asset pipeline, so add the following to app/assets/javascripts/application.js (right after <tt>//= require jquery_ujs</tt>):
45
-
46
- //= require jquery.form
47
- //= require jquery.remotipart
48
-
49
51
  == Usage
50
52
 
51
53
  * For multipart / forms with file inputs, set your form_for to remote as you would for a normal ajax form:
52
54
  :remote => true
53
55
  * When Javascript is enabled in the user's browser, the form, including the file, will be submitted asynchronously to your controller with:
54
56
  :format == 'js'
55
- * In the JS response template for your controller action, wrap all of your response code in one remotipart_response block:
56
- <%= remotipart_response do %> All Javascript response code goes here <% end %>
57
- * The options available to the text_area_tag[http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html#method-i-text_area_tag] can be passed to remotipart_response for further control over the response. For instance:
58
- <%= remotipart_response({:escape => false}) do %> All Javascript response code goes here <% end %>
59
- * Anything inside the +remotipart_response+ block will be rendered normally, unless the request actually did come from a remotipart-submitted form. So <tt>js.erb</tt> responses can be shared between remotipart and non-remotipart forms.
60
- <%= remotipart_response do %>
61
- // do stuff here
62
- <% end %>
63
57
  * If you need to determine if a particular request was made via a remotipart-enabled form...
64
58
  * from your Rails controller or view:
65
59
 
@@ -93,14 +87,12 @@ sample_controller.rb
93
87
  end
94
88
 
95
89
  create.js.erb
96
- <%= remotipart_response do %>
97
- // Display a Javascript alert
98
- alert('success!');
99
- <% if remotipart_submitted? %>
100
- alert('submitted via remotipart')
101
- <% else %>
102
- alert('submitted via native jquery-ujs')
103
- <% end %>
90
+ // Display a Javascript alert
91
+ alert('success!');
92
+ <% if remotipart_submitted? %>
93
+ alert('submitted via remotipart')
94
+ <% else %>
95
+ alert('submitted via native jquery-ujs')
104
96
  <% end %>
105
97
 
106
98
  == Note on Patches/Pull Requests
@@ -115,6 +107,36 @@ create.js.erb
115
107
  (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
116
108
  * Send me a pull request. Bonus points for topic branches.
117
109
 
110
+ == Tests
111
+
112
+ Because of the nature of AJAX file uploads and certain browser restrictions, we could not simply create unit tests (using qunit or jasmine)
113
+ to test the file upload functionality of remotipart (since the browsers running those test suites won't allow us to set the target of a file
114
+ upload input using javascript). So, instead we created a demo Rails app using remotipart with all remotipart functionality tested using RSpec and Capybara.
115
+
116
+ * {Demo Rails App with Tests}[https://github.com/JangoSteve/Rails-jQuery-Demo/tree/remotipart]
117
+ * {Tests}[https://github.com/JangoSteve/Rails-jQuery-Demo/blob/remotipart/spec/integration/comments_spec.rb]
118
+
119
+ To run tests:
120
+
121
+ Clone the remotipart branch of the demo app
122
+ git clone -b remotipart git://github.com/JangoSteve/Rails-jQuery-Demo.git
123
+
124
+ Install the dependencies
125
+ bundle install
126
+
127
+ Run the tests
128
+ bundle exec rspec spec/
129
+
130
+ If you need to test your own changes to remotipart, just update the Gemfile with your own fork/branch of remotipart:
131
+
132
+ gem 'remotipart', :git => 'git://github.com/MY_FORK/remotipart.git', :branch => 'MY_BRANCH'
133
+
134
+ == Special Thanks
135
+
136
+ Thank you to Greg Leppert for writing the original version of this gem and providing inspiration for the gem in its current incarnation.
137
+
138
+ Thank you to {Adam Kerr}[https://github.com/ajrkerr] for helping move over to the simpler jQuery 1.6-compatible iframe-transport.js and for helping write the rack middleware, making remotipart even easier to use in Rails.
139
+
118
140
  == Copyright
119
141
 
120
- Copyright (c) 2011 Greg Leppert, Steve Schwartz. See LICENSE for details.
142
+ Copyright (c) 2011 {Steve Schwartz}[https://github.com/JangoSteve], {Greg Leppert}[https://github.com/leppert]. See LICENSE for details.
@@ -4,12 +4,12 @@ module Remotipart
4
4
  module Generators
5
5
  class InstallGenerator < ::Rails::Generators::Base
6
6
 
7
- desc "This generator installs Form.js #{Remotipart::Rails::FORMJS_VERSION} and Remotipart #{Remotipart::Rails::VERSION}"
7
+ desc "This generator installs IframeTransport.js #{Remotipart::Rails::IFRAMETRANSPORT_VERSION} and Remotipart #{Remotipart::Rails::VERSION}"
8
8
  source_root File.expand_path('../../../../../vendor/assets/javascripts', __FILE__)
9
9
 
10
- def install_formjs
11
- say_status "copying", "Form.js #{Remotipart::Rails::FORMJS_VERSION}", :green
12
- copy_file "jquery.form.js", "public/javascripts/jquery.form.js"
10
+ def install_iframe_transport
11
+ say_status "copying", "IframeTransport.js #{Remotipart::Rails::IFRAMETRANSPORT_VERSION}", :green
12
+ copy_file "jquery.iframe-transport.js", "public/javascripts/jquery.iframe-transport.js"
13
13
  end
14
14
 
15
15
  def install_remotipart
@@ -0,0 +1,33 @@
1
+ module Remotipart
2
+
3
+ # A middleware to look for our form parameters and
4
+ # encourage Rails to respond with the requested format
5
+ class Middleware
6
+ def initialize app
7
+ @app = app
8
+ end
9
+
10
+ def call env
11
+ # For some reason, in Rails 3.0, `env['rack.request.form_hash']`
12
+ # isn't populated unless we manually initialize a new Rack::Request
13
+ # and call the `POST` method on it
14
+ if ::Rails.version < "3.1"
15
+ Rack::Request.new(env).POST
16
+ end
17
+ params = env['rack.request.form_hash']
18
+
19
+ # This was using an iframe transport, and is therefore an XHR
20
+ # This is required if we're going to override the http_accept
21
+ if params and params['X-Requested-With'] == 'IFrame'
22
+ env['HTTP_X_REQUESTED_WITH'] = 'xmlhttprequest'
23
+ end
24
+
25
+ # Override the accepted format, because it isn't what we really want
26
+ if params and params['X-Http-Accept']
27
+ env['HTTP_ACCEPT'] = params['X-Http-Accept']
28
+ end
29
+
30
+ @app.call(env)
31
+ end
32
+ end
33
+ end
@@ -10,6 +10,11 @@ module Remotipart
10
10
 
11
11
  initializer "remotipart.controller_helper" do
12
12
  ActionController::Base.send :include, RequestHelper
13
+ ActionController::Base.send :include, RenderOverrides
14
+ end
15
+
16
+ initializer "remotipart.include_middelware" do
17
+ config.app_middleware.insert_after ActionDispatch::ParamsParser, Middleware
13
18
  end
14
19
  end
15
20
 
@@ -5,7 +5,7 @@ module Remotipart
5
5
  class Railtie < ::Rails::Railtie
6
6
  config.before_configuration do
7
7
  # Files to be added to :defaults
8
- FILES = ['jquery.form', 'jquery.remotipart']
8
+ FILES = ['jquery.iframe-transport', 'jquery.remotipart']
9
9
 
10
10
  # Figure out where rails.js (aka jquery_ujs.js if install by jquery-rails gem) is
11
11
  # in the :defaults array
@@ -29,6 +29,11 @@ module Remotipart
29
29
 
30
30
  initializer "remotipart.controller_helper" do
31
31
  ActionController::Base.send :include, RequestHelper
32
+ ActionController::Base.send :include, RenderOverrides
33
+ end
34
+
35
+ initializer "remotipart.include_middelware" do
36
+ config.app_middleware.insert_after ActionDispatch::ParamsParser, Middleware
32
37
  end
33
38
  end
34
39
 
@@ -1,6 +1,6 @@
1
1
  module Remotipart
2
2
  module Rails
3
- VERSION = "0.4.2"
4
- FORMJS_VERSION = "2.84"
3
+ VERSION = "1.0"
4
+ IFRAMETRANSPORT_VERSION = "07.05.2011"
5
5
  end
6
6
  end
@@ -0,0 +1,15 @@
1
+ module Remotipart
2
+
3
+ #Responder used to automagically wrap any non-xml replies in a text-area
4
+ # as expected by iframe-transport
5
+ module RenderOverrides
6
+ def render *args
7
+ super
8
+ if remotipart_submitted?
9
+ response.body = %{<textarea data-type=\"#{content_type}\">#{response.body}</textarea>}
10
+ response.content_type = Mime::HTML
11
+ end
12
+ response_body
13
+ end
14
+ end
15
+ end
@@ -3,6 +3,7 @@ module Remotipart
3
3
  def remotipart_submitted?
4
4
  params[:remotipart_submitted] ? true : false
5
5
  end
6
+
6
7
  alias :remotipart_requested? :remotipart_submitted?
7
8
  end
8
9
  end
@@ -1,13 +1,9 @@
1
1
  module Remotipart
2
2
  module ViewHelper
3
+ #No longer used
4
+ #Retrained to prevent issues while updating
3
5
  def remotipart_response(options = {}, &block)
4
- content = with_output_buffer(&block)
5
- if remotipart_submitted?
6
- response.content_type = Mime::HTML
7
- text_area_tag('remotipart_response', String.new(content), options)
8
- else
9
- content
10
- end
6
+ with_output_buffer(&block)
11
7
  end
12
8
  end
13
9
  end
data/lib/remotipart.rb CHANGED
@@ -1,3 +1,5 @@
1
1
  require 'remotipart/view_helper'
2
2
  require 'remotipart/request_helper'
3
+ require 'remotipart/render_overrides'
4
+ require 'remotipart/middleware'
3
5
  require 'remotipart/rails' if defined?(Rails)
data/remotipart.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{remotipart}
8
- s.version = "0.4.2"
8
+ s.version = "1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Greg Leppert", "Steve Schwartz"]
@@ -28,16 +28,18 @@ Gem::Specification.new do |s|
28
28
  "VERSION_COMPATIBILITY.rdoc",
29
29
  "lib/generators/remotipart/install/install_generator.rb",
30
30
  "lib/remotipart.rb",
31
+ "lib/remotipart/middleware.rb",
31
32
  "lib/remotipart/rails.rb",
32
33
  "lib/remotipart/rails/engine.rb",
33
34
  "lib/remotipart/rails/railtie.rb",
34
35
  "lib/remotipart/rails/version.rb",
36
+ "lib/remotipart/render_overrides.rb",
35
37
  "lib/remotipart/request_helper.rb",
36
38
  "lib/remotipart/view_helper.rb",
37
39
  "remotipart.gemspec",
38
40
  "test/helper.rb",
39
41
  "test/test_remotipart.rb",
40
- "vendor/assets/javascripts/jquery.form.js",
42
+ "vendor/assets/javascripts/jquery.iframe-transport.js",
41
43
  "vendor/assets/javascripts/jquery.remotipart.js"
42
44
  ]
43
45
  s.homepage = %q{http://www.alfajango.com/blog/remotipart-rails-gem/}
@@ -0,0 +1,233 @@
1
+ // This [jQuery](http://jquery.com/) plugin implements an `<iframe>`
2
+ // [transport](http://api.jquery.com/extending-ajax/#Transports) so that
3
+ // `$.ajax()` calls support the uploading of files using standard HTML file
4
+ // input fields. This is done by switching the exchange from `XMLHttpRequest` to
5
+ // a hidden `iframe` element containing a form that is submitted.
6
+
7
+ // The [source for the plugin](http://github.com/cmlenz/jquery-iframe-transport)
8
+ // is available on [Github](http://github.com/) and dual licensed under the MIT
9
+ // or GPL Version 2 licenses.
10
+
11
+ // ## Usage
12
+
13
+ // To use this plugin, you simply add a `iframe` option with the value `true`
14
+ // to the Ajax settings an `$.ajax()` call, and specify the file fields to
15
+ // include in the submssion using the `files` option, which can be a selector,
16
+ // jQuery object, or a list of DOM elements containing one or more
17
+ // `<input type="file">` elements:
18
+
19
+ // $("#myform").submit(function() {
20
+ // $.ajax(this.action, {
21
+ // files: $(":file", this),
22
+ // iframe: true
23
+ // }).complete(function(data) {
24
+ // console.log(data);
25
+ // });
26
+ // });
27
+
28
+ // The plugin will construct a hidden `<iframe>` element containing a copy of
29
+ // the form the file field belongs to, will disable any form fields not
30
+ // explicitly included, submit that form, and process the response.
31
+
32
+ // If you want to include other form fields in the form submission, include them
33
+ // in the `data` option, and set the `processData` option to `false`:
34
+
35
+ // $("#myform").submit(function() {
36
+ // $.ajax(this.action, {
37
+ // data: $(":text", this).serializeArray(),
38
+ // files: $(":file", this),
39
+ // iframe: true,
40
+ // processData: false
41
+ // }).complete(function(data) {
42
+ // console.log(data);
43
+ // });
44
+ // });
45
+
46
+ // ### The Server Side
47
+
48
+ // If the response is not HTML or XML, you (unfortunately) need to apply some
49
+ // trickery on the server side. To send back a JSON payload, send back an HTML
50
+ // `<textarea>` element with a `data-type` attribute that contains the MIME
51
+ // type, and put the actual payload in the textarea:
52
+
53
+ // <textarea data-type="application/json">
54
+ // {"ok": true, "message": "Thanks so much"}
55
+ // </textarea>
56
+
57
+ // The iframe transport plugin will detect this and attempt to apply the same
58
+ // conversions that jQuery applies to regular responses. That means for the
59
+ // example above you should get a Javascript object as the `data` parameter of
60
+ // the `complete` callback, with the properties `ok: true` and
61
+ // `message: "Thanks so much"`.
62
+
63
+ // ### Compatibility
64
+
65
+ // This plugin has primarily been tested on Safari 5, Firefox 4, and Internet
66
+ // Explorer all the way back to version 6. While I haven't found any issues with
67
+ // it so far, I'm fairly sure it still doesn't work around all the quirks in all
68
+ // different browsers. But the code is still pretty simple overall, so you
69
+ // should be able to fix it and contribute a patch :)
70
+
71
+ // ## Annotated Source
72
+
73
+ (function($, undefined) {
74
+
75
+ // Register a prefilter that checks whether the `iframe` option is set, and
76
+ // switches to the iframe transport if it is `true`.
77
+ $.ajaxPrefilter(function(options, origOptions, jqXHR) {
78
+ if (options.iframe) {
79
+ return "iframe";
80
+ }
81
+ });
82
+
83
+ // Register an iframe transport, independent of requested data type. It will
84
+ // only activate when the "files" option has been set to a non-empty list of
85
+ // enabled file inputs.
86
+ $.ajaxTransport("iframe", function(options, origOptions, jqXHR) {
87
+ var form = null,
88
+ iframe = null,
89
+ origAction = null,
90
+ origTarget = null,
91
+ origEnctype = null,
92
+ addedFields = [],
93
+ disabledFields = [],
94
+ files = $(options.files).filter(":file:enabled");
95
+
96
+ // This function gets called after a successful submission or an abortion
97
+ // and should revert all changes made to the page to enable the
98
+ // submission via this transport.
99
+ function cleanUp() {
100
+ $(addedFields).each(function() {
101
+ this.remove();
102
+ });
103
+ $(disabledFields).each(function() {
104
+ this.disabled = false;
105
+ });
106
+ form.attr("action", origAction || "")
107
+ .attr("target", origTarget || "")
108
+ .attr("enctype", origEnctype || "");
109
+ iframe.attr("src", "javascript:false;").remove();
110
+ }
111
+
112
+ // Remove "iframe" from the data types list so that further processing is
113
+ // based on the content type returned by the server, without attempting an
114
+ // (unsupported) conversion from "iframe" to the actual type.
115
+ options.dataTypes.shift();
116
+
117
+ if (files.length) {
118
+ // Determine the form the file fields belong to, and make sure they all
119
+ // actually belong to the same form.
120
+ files.each(function() {
121
+ if (form !== null && this.form !== form) {
122
+ jQuery.error("All file fields must belong to the same form");
123
+ }
124
+ form = this.form;
125
+ });
126
+ form = $(form);
127
+
128
+ // Store the original form attributes that we'll be replacing temporarily.
129
+ origAction = form.attr("action");
130
+ origTarget = form.attr("target");
131
+ origEnctype = form.attr("enctype");
132
+
133
+ // We need to disable all other inputs in the form so that they don't get
134
+ // included in the submitted data unexpectedly.
135
+ form.find(":input:not(:submit)").each(function() {
136
+ if (!this.disabled && (this.type != "file" || files.index(this) < 0)) {
137
+ this.disabled = true;
138
+ disabledFields.push(this);
139
+ }
140
+ });
141
+
142
+ // If there is any additional data specified via the `data` option,
143
+ // we add it as hidden fields to the form. This (currently) requires
144
+ // the `processData` option to be set to false so that the data doesn't
145
+ // get serialized to a string.
146
+ if (typeof(options.data) === "string" && options.data.length > 0) {
147
+ jQuery.error("data must not be serialized");
148
+ }
149
+ $.each(options.data || {}, function(name, value) {
150
+ if ($.isPlainObject(value)) {
151
+ name = value.name;
152
+ value = value.value;
153
+ }
154
+ addedFields.push($("<input type='hidden'>").attr("name", name)
155
+ .attr("value", value).appendTo(form));
156
+ });
157
+
158
+ // Add a hidden `X-Requested-With` field with the value `IFrame` to the
159
+ // field, to help server-side code to determine that the upload happened
160
+ // through this transport.
161
+ addedFields.push($("<input type='hidden' name='X-Requested-With'>")
162
+ .attr("value", "IFrame").appendTo(form));
163
+
164
+ // Borrowed straight from the JQuery source
165
+ // Provides a way of specifying the accepted data type similar to HTTP_ACCEPTS
166
+ accepts = options.dataTypes[ 0 ] && options.accepts[ options.dataTypes[0] ] ?
167
+ options.accepts[ options.dataTypes[0] ] + ( options.dataTypes[ 0 ] !== "*" ? ", */*; q=0.01" : "" ) :
168
+ options.accepts[ "*" ]
169
+
170
+ addedFields.push($("<input type='hidden' name='X-Http-Accept'>")
171
+ .attr("value", accepts).appendTo(form));
172
+
173
+ return {
174
+
175
+ // The `send` function is called by jQuery when the request should be
176
+ // sent.
177
+ send: function(headers, completeCallback) {
178
+ iframe = $("<iframe src='javascript:false;' name='iframe-" + $.now()
179
+ + "' style='display:none'></iframe>");
180
+
181
+ // The first load event gets fired after the iframe has been injected
182
+ // into the DOM, and is used to prepare the actual submission.
183
+ iframe.bind("load", function() {
184
+
185
+ // The second load event gets fired when the response to the form
186
+ // submission is received. The implementation detects whether the
187
+ // actual payload is embedded in a `<textarea>` element, and
188
+ // prepares the required conversions to be made in that case.
189
+ iframe.unbind("load").bind("load", function() {
190
+
191
+ var doc = this.contentWindow ? this.contentWindow.document :
192
+ (this.contentDocument ? this.contentDocument : this.document),
193
+ root = doc.documentElement ? doc.documentElement : doc.body,
194
+ textarea = root.getElementsByTagName("textarea")[0],
195
+ type = textarea ? textarea.getAttribute("data-type") : null;
196
+
197
+ var status = 200,
198
+ statusText = "OK",
199
+ responses = { text: type ? textarea.value : root ? root.innerHTML : null },
200
+ headers = "Content-Type: " + (type || "text/html")
201
+
202
+ completeCallback(status, statusText, responses, headers);
203
+
204
+ setTimeout(cleanUp, 50);
205
+ });
206
+
207
+ // Now that the load handler has been set up, reconfigure and
208
+ // submit the form.
209
+ form.attr("action", options.url)
210
+ .attr("target", iframe.attr("name"))
211
+ .attr("enctype", "multipart/form-data")
212
+ .get(0).submit();
213
+ });
214
+
215
+ // After everything has been set up correctly, the iframe gets
216
+ // injected into the DOM so that the submission can be initiated.
217
+ iframe.insertAfter(form);
218
+ },
219
+
220
+ // The `abort` function is called by jQuery when the request should be
221
+ // aborted.
222
+ abort: function() {
223
+ if (iframe !== null) {
224
+ iframe.unbind("load").attr("src", "javascript:false;");
225
+ cleanUp();
226
+ }
227
+ }
228
+
229
+ };
230
+ }
231
+ });
232
+
233
+ })(jQuery);