Added support for upload progress indicators in Apache and lighttpd 1.4.x...

Added support for upload progress indicators in Apache and lighttpd 1.4.x (won't work in WEBrick or lighttpd 1.3.x) #1475 [Sean Treadway]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1552 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
上级 96e590ca
*SVN* *SVN*
* Added support for upload progress indicators in Apache and lighttpd 1.4.x (won't work in WEBrick or lighttpd 1.3.x) #1475 [Sean Treadway]
See http://sean.treadway.info/files/howto-upload-progress-2.mov for example.
* Added support for graceful error handling of Ajax calls #1217 [Jamis Buck/Thomas Fuchs]. Example: * Added support for graceful error handling of Ajax calls #1217 [Jamis Buck/Thomas Fuchs]. Example:
link_to_remote( link_to_remote(
......
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
require 'action_controller/caching' require 'action_controller/caching'
require 'action_controller/components' require 'action_controller/components'
require 'action_controller/verification' require 'action_controller/verification'
require 'action_controller/upload_progress'
require 'action_controller/streaming' require 'action_controller/streaming'
require 'action_controller/auto_complete' require 'action_controller/auto_complete'
...@@ -69,6 +70,7 @@ ...@@ -69,6 +70,7 @@
include ActionController::Caching include ActionController::Caching
include ActionController::Components include ActionController::Components
include ActionController::Verification include ActionController::Verification
include ActionController::UploadProgress
include ActionController::Streaming include ActionController::Streaming
include ActionController::AutoComplete include ActionController::AutoComplete
end end
...@@ -508,12 +508,24 @@ def render_to_string(options = {}) #:doc: ...@@ -508,12 +508,24 @@ def render_to_string(options = {}) #:doc:
return result return result
end end
# Clears the rendered results, allowing for another render or redirect to be performed. # Clears the rendered results, allowing for another render to be performed.
def erase_render_results #:nodoc: def erase_render_results #:nodoc:
@response.body = nil @response.body = nil
@performed_render = false @performed_render = false
end end
# Clears the redirected results from the headers, resetting the status to 200 and returns
# the URL that was used to redirect or nil if there was no redirected URL
# Note that +redirect_to+ will change the body of the response to indicate a redirection.
# The response body is not reset here, see +erase_render_results+
def erase_redirect_results #:nodoc:
@performed_redirect = false
response.redirected_to = nil
response.redirected_to_method_params = nil
response.headers['Status'] = DEFAULT_RENDER_STATUS_CODE
response.headers.delete('location')
end
def rewrite_options(options) def rewrite_options(options)
if defaults = default_url_options(options) if defaults = default_url_options(options)
defaults.merge(options) defaults.merge(options)
......
...@@ -6,6 +6,8 @@ module QueryExtension ...@@ -6,6 +6,8 @@ module QueryExtension
# Handles multipart forms (in particular, forms that involve file uploads). # Handles multipart forms (in particular, forms that involve file uploads).
# Reads query parameters in the @params field, and cookies into @cookies. # Reads query parameters in the @params field, and cookies into @cookies.
def initialize_query() def initialize_query()
@cookies = CGI::Cookie::parse((env_table['HTTP_COOKIE'] || env_table['COOKIE']))
if boundary = multipart_form_boundary if boundary = multipart_form_boundary
@multipart = true @multipart = true
@params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH'])) @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
...@@ -13,8 +15,6 @@ def initialize_query() ...@@ -13,8 +15,6 @@ def initialize_query()
@multipart = false @multipart = false
@params = CGI::parse(read_query_params || "") @params = CGI::parse(read_query_params || "")
end end
@cookies = CGI::Cookie::parse((env_table['HTTP_COOKIE'] || env_table['COOKIE']))
end end
private private
...@@ -28,16 +28,32 @@ def multipart_form_boundary ...@@ -28,16 +28,32 @@ def multipart_form_boundary
end end
end end
def read_params_from_query
if defined? MOD_RUBY
Apache::request.args || ''
else
# fixes CGI querystring parsing for POSTs
if env_table['QUERY_STRING'].blank? && !env_table['REQUEST_URI'].blank?
env_table['QUERY_STRING'] = env_table['REQUEST_URI'].split('?', 2)[1] || ''
end
env_table['QUERY_STRING']
end
end
def read_params_from_post
stdinput.binmode if stdinput.respond_to?(:binmode)
content = stdinput.read(Integer(env_table['CONTENT_LENGTH'])) || ''
env_table['RAW_POST_DATA'] = content.split("&_").first.to_s.freeze # &_ is a fix for Safari Ajax postings that always append \000
end
def read_query_params def read_query_params
case env_table['REQUEST_METHOD'].to_s.upcase case env_table['REQUEST_METHOD'].to_s.upcase
when 'CMD' when 'CMD'
read_from_cmdline read_from_cmdline
when 'POST', 'PUT' when 'POST', 'PUT'
stdinput.binmode if stdinput.respond_to?(:binmode) read_params_from_post
content = stdinput.read(Integer(env_table['CONTENT_LENGTH'])) || ''
env_table['RAW_POST_DATA'] = content.split("&_").first.to_s.freeze # &_ is a fix for Safari Ajax postings that always append \000
else # when 'GET', 'HEAD', 'DELETE', 'OPTIONS' else # when 'GET', 'HEAD', 'DELETE', 'OPTIONS'
(defined?(MOD_RUBY) ? Apache::request.args : env_table['QUERY_STRING']) || '' read_params_from_query
end end
end end
end # module QueryExtension end # module QueryExtension
......
...@@ -124,7 +124,7 @@ def periodically_call_remote(options = {}) ...@@ -124,7 +124,7 @@ def periodically_call_remote(options = {})
code = "new PeriodicalExecuter(function() {#{remote_function(options)}}, #{frequency})" code = "new PeriodicalExecuter(function() {#{remote_function(options)}}, #{frequency})"
content_tag("script", code, options[:html_options] || {}) content_tag("script", code, options[:html_options] || {})
end end
# Returns a form tag that will submit using XMLHttpRequest in the background instead of the regular # Returns a form tag that will submit using XMLHttpRequest in the background instead of the regular
# reloading POST arrangement. Even though it's using Javascript to serialize the form elements, the form submission # reloading POST arrangement. Even though it's using Javascript to serialize the form elements, the form submission
# will work just like a regular submission as viewed by the receiving side (all elements available in @params). # will work just like a regular submission as viewed by the receiving side (all elements available in @params).
...@@ -373,6 +373,7 @@ def options_for_ajax(options) ...@@ -373,6 +373,7 @@ def options_for_ajax(options)
js_options['asynchronous'] = options[:type] != :synchronous js_options['asynchronous'] = options[:type] != :synchronous
js_options['method'] = method_option_to_s(options[:method]) if options[:method] js_options['method'] = method_option_to_s(options[:method]) if options[:method]
js_options['insertion'] = "Insertion.#{options[:position].to_s.camelize}" if options[:position] js_options['insertion'] = "Insertion.#{options[:position].to_s.camelize}" if options[:position]
js_options['script'] = options[:script] == true if options[:script]
if options[:form] if options[:form]
js_options['parameters'] = 'Form.serialize(this)' js_options['parameters'] = 'Form.serialize(this)'
...@@ -382,7 +383,7 @@ def options_for_ajax(options) ...@@ -382,7 +383,7 @@ def options_for_ajax(options)
options_for_javascript(js_options) options_for_javascript(js_options)
end end
def method_option_to_s(method) def method_option_to_s(method)
(method.is_a?(String) and !method.index("'").nil?) ? method : "'#{method}'" (method.is_a?(String) and !method.index("'").nil?) ? method : "'#{method}'"
end end
......
...@@ -254,6 +254,7 @@ Ajax.Updater.prototype = (new Ajax.Base()).extend({ ...@@ -254,6 +254,7 @@ Ajax.Updater.prototype = (new Ajax.Base()).extend({
failure: container.failure ? $(container.failure) : null failure: container.failure ? $(container.failure) : null
} }
this.script_re = /<script.*?>((?:\n|.)*?)<\/script>/im;
this.setOptions(options); this.setOptions(options);
if (this.options.asynchronous) { if (this.options.asynchronous) {
...@@ -271,23 +272,79 @@ Ajax.Updater.prototype = (new Ajax.Base()).extend({ ...@@ -271,23 +272,79 @@ Ajax.Updater.prototype = (new Ajax.Base()).extend({
var receiver = var receiver =
(this.request.transport.status == 200) ? (this.request.transport.status == 200) ?
this.containers.success : this.containers.failure; this.containers.success : this.containers.failure;
var response = this.request.transport.responseText.replace(
this.script_re, '');
var scripts = this.request.transport.responseText.match(
this.script_re);
if (receiver) { if (receiver) {
if (this.options.insertion) { if (this.options.insertion) {
new this.options.insertion(receiver, new this.options.insertion(receiver, response);
this.request.transport.responseText);
} else { } else {
receiver.innerHTML = this.request.transport.responseText; receiver.innerHTML = response;
} }
} }
if (this.request.transport.status == 200 && this.onComplete) { if (this.request.transport.status == 200) {
setTimeout((function() {this.onComplete( if (this.onComplete) {
this.request.transport)}).bind(this), 10); setTimeout((function() {this.onComplete(
this.request.transport)}).bind(this), 10);
}
if (this.options.script && scripts) {
setTimeout((function() { eval(scripts[1]) }).bind(this), 10);
}
}
}
});
Ajax.PeriodicalUpdater = Class.create();
Ajax.PeriodicalUpdater.prototype = (new Ajax.Base()).extend({
initialize: function(container, url, options) {
this.setOptions(options);
this.onComplete = this.options.onComplete;
this.frequency = (this.options.frequency || 2);
this.decay = 1;
this.updater = {};
this.container = container;
this.url = url;
this.start();
},
start: function() {
this.options.onComplete = this.updateComplete.bind(this);
this.onTimerEvent();
},
stop: function() {
this.updater.onComplete = undefined;
clearTimeout(this.timer);
(this.onComplete || Ajax.emptyFunction).apply(this, arguments);
},
updateComplete: function(request) {
if (this.options.decay) {
this.decay = (request.responseText == this.lastText ?
this.decay * this.options.decay : 1);
this.lastText = request.responseText;
} }
this.timer = setTimeout(this.onTimerEvent.bind(this),
this.decay * this.frequency * 1000);
},
onTimerEvent: function() {
this.updater = new Ajax.Updater(this.container, this.url, this.options);
} }
}); });
/*--------------------------------------------------------------------------*/
document.getElementsByClassName = function(className) { document.getElementsByClassName = function(className) {
var children = document.getElementsByTagName('*') || document.all; var children = document.getElementsByTagName('*') || document.all;
var elements = new Array(); var elements = new Array();
......
...@@ -51,3 +51,24 @@ a:hover { color: #fff; background-color:#000; } ...@@ -51,3 +51,24 @@ a:hover { color: #fff; background-color:#000; }
font-size: 12px; font-size: 12px;
list-style: square; list-style: square;
} }
div.uploadStatus {
margin: 5px;
}
div.progressBar {
margin: 5px;
}
div.progressBar div.border {
background-color: #fff;
border: 1px solid grey;
width: 100%;
}
div.progressBar div.background {
background-color: #333;
height: 18px;
width: 0%;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册