From 37bf5c5aeec37e5d6fcdf215308887534fae5714 Mon Sep 17 00:00:00 2001 From: Dennis Collinson Date: Mon, 12 Mar 2012 17:53:28 -0700 Subject: [PATCH] REMOTIPART WIP --- Gemfile | 1 + Gemfile.lock | 4 +- app/controllers/photos_controller.rb | 63 ++- app/controllers/posts_controller.rb | 4 +- app/controllers/status_messages_controller.rb | 3 +- app/views/photos/_new_photo.haml | 2 +- app/views/posts/new.html.haml | 15 + config/assets.yml | 2 + public/javascripts/rails.js | 480 ++++++++++++------ .../vendor/jquery.iframe-transport.js | 233 +++++++++ .../javascripts/vendor/jquery.remotipart.js | 69 +++ 11 files changed, 693 insertions(+), 183 deletions(-) create mode 100644 app/views/posts/new.html.haml create mode 100644 public/javascripts/vendor/jquery.iframe-transport.js create mode 100644 public/javascripts/vendor/jquery.remotipart.js diff --git a/Gemfile b/Gemfile index 4f978d144..ed7eea7a6 100644 --- a/Gemfile +++ b/Gemfile @@ -17,6 +17,7 @@ gem 'rack-cors', '~> 0.2.4', :require => 'rack/cors' gem 'devise', '1.5.3' gem 'jwt' gem 'oauth2-provider', '0.0.19' +gem 'remotipart', '~> 1.0' gem 'omniauth', '1.0.1' gem 'omniauth-facebook' diff --git a/Gemfile.lock b/Gemfile.lock index 32c069695..98f6dc1e7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -162,7 +162,7 @@ GEM fixture_builder (0.3.1) activerecord (>= 2) activesupport (>= 2) - fog (1.2.0) + fog (1.3.0) builder excon (~> 0.12.0) formatador (~> 0.2.0) @@ -341,6 +341,7 @@ GEM redis (2.2.2) redis-namespace (1.0.3) redis (< 3.0.0) + remotipart (1.0.2) resque (1.20.0) multi_json (~> 1.0) redis-namespace (~> 1.0.2) @@ -513,6 +514,7 @@ DEPENDENCIES rails-i18n rails_autolink redcarpet (= 2.0.1) + remotipart (~> 1.0) resque (= 1.20.0) resque-timeout (= 1.0.0) rest-client (= 1.6.7) diff --git a/app/controllers/photos_controller.rb b/app/controllers/photos_controller.rb index c2d1978da..30556bfc6 100644 --- a/app/controllers/photos_controller.rb +++ b/app/controllers/photos_controller.rb @@ -41,21 +41,23 @@ class PhotosController < ApplicationController end def create - begin - raise unless params[:photo][:aspect_ids] + rescuing_photo_errors do |p| + if remotipart_submitted? + @photo = current_user.build_post(:photo, params[:photo]) + else + raise "not remotipart" unless params[:photo][:aspect_ids] - if params[:photo][:aspect_ids] == "all" - params[:photo][:aspect_ids] = current_user.aspects.collect{|x| x.id} - elsif params[:photo][:aspect_ids].is_a?(Hash) - params[:photo][:aspect_ids] = params[:photo][:aspect_ids].values - end + if params[:photo][:aspect_ids] == "all" + params[:photo][:aspect_ids] = current_user.aspects.collect { |x| x.id } + elsif params[:photo][:aspect_ids].is_a?(Hash) + params[:photo][:aspect_ids] = params[:photo][:aspect_ids].values + end - params[:photo][:user_file] = file_handler(params) + params[:photo][:user_file] = file_handler(params) - @photo = current_user.build_post(:photo, params[:photo]) - - if @photo.save + @photo = current_user.build_post(:photo, params[:photo]) + if @photo.save aspects = current_user.aspects_from_ids(params[:photo][:aspect_ids]) unless @photo.pending @@ -65,11 +67,14 @@ class PhotosController < ApplicationController if params[:photo][:set_profile_photo] profile_params = {:image_url => @photo.url(:thumb_large), - :image_url_medium => @photo.url(:thumb_medium), - :image_url_small => @photo.url(:thumb_small)} + :image_url_medium => @photo.url(:thumb_medium), + :image_url_small => @photo.url(:thumb_small)} current_user.update_profile(profile_params) end + end + end + if @photo.save respond_to do |format| format.json{ render(:layout => false , :json => {"success" => true, "data" => @photo}.to_json )} format.html{ render(:layout => false , :json => {"success" => true, "data" => @photo}.to_json )} @@ -77,19 +82,6 @@ class PhotosController < ApplicationController else respond_with @photo, :location => photos_path, :error => message end - - rescue TypeError - message = I18n.t 'photos.create.type_error' - respond_with @photo, :location => photos_path, :error => message - - rescue CarrierWave::IntegrityError - message = I18n.t 'photos.create.integrity_error' - respond_with @photo, :location => photos_path, :error => message - - rescue RuntimeError => e - message = I18n.t 'photos.create.runtime_error' - respond_with @photo, :location => photos_path, :error => message - raise e end end @@ -200,4 +192,23 @@ class PhotosController < ApplicationController file end end + + + def rescuing_photo_errors + begin + yield + rescue TypeError + message = I18n.t 'photos.create.type_error' + respond_with @photo, :location => photos_path, :error => message + + rescue CarrierWave::IntegrityError + message = I18n.t 'photos.create.integrity_error' + respond_with @photo, :location => photos_path, :error => message + + rescue RuntimeError => e + message = I18n.t 'photos.create.runtime_error' + respond_with @photo, :location => photos_path, :error => message + raise e + end + end end diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 7fff7c82e..e4cb4b104 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -16,7 +16,7 @@ class PostsController < ApplicationController :xml def new - render :text => "", :layout => true + end def show @@ -40,7 +40,7 @@ class PostsController < ApplicationController format.xml{ render :xml => @post.to_diaspora_xml } format.mobile{render 'posts/show.mobile.haml'} format.json{ render :json => PostPresenter.new(@post, current_user).to_json } - format.any{render 'posts/show.html.haml'} + format.any{render 'posts/show.html.haml'} end else diff --git a/app/controllers/status_messages_controller.rb b/app/controllers/status_messages_controller.rb index 5953993a9..b6764b890 100644 --- a/app/controllers/status_messages_controller.rb +++ b/app/controllers/status_messages_controller.rb @@ -53,7 +53,8 @@ class StatusMessagesController < ApplicationController receiving_services = Service.titles(services) current_user.dispatch_post(@status_message, :url => short_post_url(@status_message.guid), :service_types => receiving_services) - + + #this is done implicitly, somewhere else, apparently, says max. :'( # @status_message.photos.each do |photo| # current_user.dispatch_post(photo) # end diff --git a/app/views/photos/_new_photo.haml b/app/views/photos/_new_photo.haml index 4da494f18..95fa60e27 100644 --- a/app/views/photos/_new_photo.haml +++ b/app/views/photos/_new_photo.haml @@ -83,4 +83,4 @@ }); } - createUploader(); + createUploader(); \ No newline at end of file diff --git a/app/views/posts/new.html.haml b/app/views/posts/new.html.haml new file mode 100644 index 000000000..a20a287a2 --- /dev/null +++ b/app/views/posts/new.html.haml @@ -0,0 +1,15 @@ += form_for Photo.new, :html => { :multipart => true }, :remote => true do |f| + = f.label :user_file + = f.file_field :user_file + = f.submit + +:javascript + $(function(){ + console.log($('#new_photo')) + $('#new_photo').bind('ajax:success', function(event, data) { + alert("happy day") + console.log(data) + console.log(new Backbone.Model(data)); // Your newly created Backbone.js model + + }); + }); \ No newline at end of file diff --git a/config/assets.yml b/config/assets.yml index 5212580a3..e7086b92b 100644 --- a/config/assets.yml +++ b/config/assets.yml @@ -25,6 +25,8 @@ javascripts: - public/javascripts/vendor/timeago.js - public/javascripts/vendor/facebox.js - public/javascripts/vendor/underscore.js + - public/javascripts/vendor/jquery.iframe-transport.js + - public/javascripts/vendor/jquery.remotipart.js - public/javascripts/vendor/jquery.events.input.js - public/javascripts/vendor/jquery.elastic.js - public/javascripts/vendor/jquery.mentionsInput.js diff --git a/public/javascripts/rails.js b/public/javascripts/rails.js index 2fbb9b83c..028b0fc62 100644 --- a/public/javascripts/rails.js +++ b/public/javascripts/rails.js @@ -1,198 +1,374 @@ -/* Clear form plugin - called using $("elem").clearForm(); */ -$.fn.clearForm = function() { - return this.each(function() { - if ($(this).is('form')) { - return $(':input', this).clearForm(); - } - if ($(this).hasClass('clear_on_submit') || $(this).is(':text') || $(this).is(':password') || $(this).is('textarea')) { - $(this).val(''); - } else if ($(this).is(':checkbox') || $(this).is(':radio')) { - $(this).attr('checked', false); - } else if ($(this).is('select')) { - this.selectedIndex = -1; - } else if ($(this).attr('name') == 'photos[]') { - $(this).val(''); - } - $(this).blur(); - }); -}; +(function($, undefined) { /** * Unobtrusive scripting adapter for jQuery * - * Requires jQuery 1.4.3 or later. + * Requires jQuery 1.6.0 or later. * https://github.com/rails/jquery-ujs + + * Uploading file using rails.js + * ============================= + * + * By default, browsers do not allow files to be uploaded via AJAX. As a result, if there are any non-blank file fields + * in the remote form, this adapter aborts the AJAX submission and allows the form to submit through standard means. + * + * The `ajax:aborted:file` event allows you to bind your own handler to process the form submission however you wish. + * + * Ex: + * $('form').live('ajax:aborted:file', function(event, elements){ + * // Implement own remote file-transfer handler here for non-blank file inputs passed in `elements`. + * // Returning false in this handler tells rails.js to disallow standard form submission + * return false; + * }); + * + * The `ajax:aborted:file` event is fired when a file-type input is detected with a non-blank value. + * + * Third-party tools can use this hook to detect when an AJAX file upload is attempted, and then use + * techniques like the iframe method to upload the file instead. + * + * Required fields in rails.js + * =========================== + * + * If any blank required inputs (required="required") are detected in the remote form, the whole form submission + * is canceled. Note that this is unlike file inputs, which still allow standard (non-AJAX) form submission. + * + * The `ajax:aborted:required` event allows you to bind your own handler to inform the user of blank required inputs. + * + * !! Note that Opera does not fire the form's submit event if there are blank required inputs, so this event may never + * get fired in Opera. This event is what causes other browsers to exhibit the same submit-aborting behavior. + * + * Ex: + * $('form').live('ajax:aborted:required', function(event, elements){ + * // Returning false in this handler tells rails.js to submit the form anyway. + * // The blank required inputs are passed to this function in `elements`. + * return ! confirm("Would you like to submit the form with missing info?"); + * }); */ -(function($) { - // Make sure that every Ajax request sends the CSRF token - function CSRFProtection(fn) { - var token = $('meta[name="csrf-token"]').attr('content'); - if (token) fn(function(xhr) { xhr.setRequestHeader('X-CSRF-Token', token) }); - } - if ($().jquery == '1.5') { // gruesome hack - var factory = $.ajaxSettings.xhr; - $.ajaxSettings.xhr = function() { - var xhr = factory(); - CSRFProtection(function(setHeader) { - var open = xhr.open; - xhr.open = function() { open.apply(this, arguments); setHeader(this) }; - }); - return xhr; - }; - } - else $(document).ajaxSend(function(e, xhr) { - CSRFProtection(function(setHeader) { setHeader(xhr) }); - }); + // Shorthand to make it a little easier to call public rails functions from within rails.js + var rails; - // Triggers an event on an element and returns the event result - function fire(obj, name, data) { - var event = new $.Event(name); - obj.trigger(event, data); - return event.result !== false; - } + $.rails = rails = { + // Link elements bound by jquery-ujs + linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote], a[data-disable-with]', - // Submits "remote" forms and links with ajax - function handleRemote(element) { - var method, url, data, - dataType = element.attr('data-type') || ($.ajaxSettings && $.ajaxSettings.dataType); + // Select elements bound by jquery-ujs + inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]', - if (element.is('form')) { - method = element.attr('method'); - url = element.attr('action'); - data = element.serializeArray(); - // memoized value from clicked submit button - var button = element.data('ujs:submit-button'); - if (button) { - data.push(button); - element.data('ujs:submit-button', null); - } - } else { - method = element.attr('data-method'); - url = element.attr('href'); - data = null; - } + // Form elements bound by jquery-ujs + formSubmitSelector: 'form', - $.ajax({ - url: url, type: method || 'GET', data: data, dataType: dataType, - // stopping the "ajax:beforeSend" event will cancel the ajax request - beforeSend: function(xhr, settings) { - if (settings.dataType === undefined) { - xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script); + // Form input elements bound by jquery-ujs + formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not(button[type])', + + // Form input elements disabled during form submission + disableSelector: 'input[data-disable-with], button[data-disable-with], textarea[data-disable-with]', + + // Form input elements re-enabled after form submission + enableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled', + + // Form required input elements + requiredInputSelector: 'input[name][required]:not([disabled]),textarea[name][required]:not([disabled])', + + // Form file input elements + fileInputSelector: 'input:file', + + // Link onClick disable selector with possible reenable after remote submission + linkDisableSelector: 'a[data-disable-with]', + + // Make sure that every Ajax request sends the CSRF token + CSRFProtection: function(xhr) { + var token = $('meta[name="csrf-token"]').attr('content'); + if (token) xhr.setRequestHeader('X-CSRF-Token', token); + }, + + // Triggers an event on an element and returns false if the event result is false + fire: function(obj, name, data) { + var event = $.Event(name); + obj.trigger(event, data); + return event.result !== false; + }, + + // Default confirm dialog, may be overridden with custom confirm dialog in $.rails.confirm + confirm: function(message) { + return confirm(message); + }, + + // Default ajax function, may be overridden with custom function in $.rails.ajax + ajax: function(options) { + return $.ajax(options); + }, + + // Submits "remote" forms and links with ajax + handleRemote: function(element) { + var method, url, data, + crossDomain = element.data('cross-domain') || null, + dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType), + options; + + if (rails.fire(element, 'ajax:before')) { + + if (element.is('form')) { + method = element.attr('method'); + url = element.attr('action'); + data = element.serializeArray(); + // memoized value from clicked submit button + var button = element.data('ujs:submit-button'); + if (button) { + data.push(button); + element.data('ujs:submit-button', null); + } + } else if (element.is(rails.inputChangeSelector)) { + method = element.data('method'); + url = element.data('url'); + data = element.serialize(); + if (element.data('params')) data = data + "&" + element.data('params'); + } else { + method = element.data('method'); + url = element.attr('href'); + data = element.data('params') || null; } - return fire(element, 'ajax:beforeSend', [xhr, settings]); - }, - success: function(data, status, xhr) { - element.trigger('ajax:success', [data, status, xhr]); - }, - complete: function(xhr, status) { - element.trigger('ajax:complete', [xhr, status]); - }, - error: function(xhr, status, error) { - element.trigger('ajax:error', [xhr, status, error]); + + options = { + type: method || 'GET', data: data, dataType: dataType, crossDomain: crossDomain, + // stopping the "ajax:beforeSend" event will cancel the ajax request + beforeSend: function(xhr, settings) { + if (settings.dataType === undefined) { + xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script); + } + return rails.fire(element, 'ajax:beforeSend', [xhr, settings]); + }, + success: function(data, status, xhr) { + alert("hella boner jamz") + element.trigger('ajax:success', [data, status, xhr]); + }, + complete: function(xhr, status) { + element.trigger('ajax:complete', [xhr, status]); + }, + error: function(xhr, status, error) { + element.trigger('ajax:error', [xhr, status, error]); + } + }; + // Only pass url to `ajax` options if not blank + if (url) { options.url = url; } + + return rails.ajax(options); + } else { + return false; } - }); - } + }, - // Handles "data-method" on links such as: - // Delete - function handleMethod(link) { - var href = link.attr('href'), - method = link.attr('data-method'), - csrf_token = $('meta[name=csrf-token]').attr('content'), - csrf_param = $('meta[name=csrf-param]').attr('content'), - form = $('
'), - metadata_input = '', - form_params = link.data('form-params'); + // Handles "data-method" on links such as: + // Delete + handleMethod: function(link) { + var href = link.attr('href'), + method = link.data('method'), + target = link.attr('target'), + csrf_token = $('meta[name=csrf-token]').attr('content'), + csrf_param = $('meta[name=csrf-param]').attr('content'), + form = $('
'), + metadata_input = ''; - if (csrf_param !== undefined && csrf_token !== undefined) { - metadata_input += ''; - } - - // support non-nested JSON encoded params for links - if (form_params != undefined) { - var params = $.parseJSON(form_params); - for (key in params) { - form.append($("").attr({"type": "hidden", "name": key, "value": params[key]})); + if (csrf_param !== undefined && csrf_token !== undefined) { + metadata_input += ''; } - } - form.hide().append(metadata_input).appendTo('body'); - form.submit(); - } + if (target) { form.attr('target', target); } - function disableFormElements(form) { - form.find('input[data-disable-with]').each(function() { - var input = $(this); - input.data('ujs:enable-with', input.val()) - .val(input.attr('data-disable-with')) - .attr('disabled', 'disabled'); - }); - } + form.hide().append(metadata_input).appendTo('body'); + form.submit(); + }, - function enableFormElements(form) { - form.find('input[data-disable-with]').each(function() { - var input = $(this); - input.val(input.data('ujs:enable-with')).removeAttr('disabled'); - }); - } + /* Disables form elements: + - Caches element value in 'ujs:enable-with' data store + - Replaces element text with value of 'data-disable-with' attribute + - Sets disabled property to true + */ + disableFormElements: function(form) { + form.find(rails.disableSelector).each(function() { + var element = $(this), method = element.is('button') ? 'html' : 'val'; + element.data('ujs:enable-with', element[method]()); + element[method](element.data('disable-with')); + element.prop('disabled', true); + }); + }, - function allowAction(element) { - var message = element.attr('data-confirm'); - return !message || (fire(element, 'confirm') && confirm(message)); - } + /* Re-enables disabled form elements: + - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`) + - Sets disabled property to false + */ + enableFormElements: function(form) { + form.find(rails.enableSelector).each(function() { + var element = $(this), method = element.is('button') ? 'html' : 'val'; + if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with')); + element.prop('disabled', false); + }); + }, - function requiredValuesMissing(form) { - var missing = false; - form.find('input[name][required]').each(function() { - if (!$(this).val()) missing = true; - }); - return missing; - } + /* For 'data-confirm' attribute: + - Fires `confirm` event + - Shows the confirmation dialog + - Fires the `confirm:complete` event - $('a[data-confirm], a[data-method], a[data-remote]').live('click.rails', function(e) { - var link = $(this); - if (!allowAction(link)) return false; + Returns `true` if no function stops the chain and user chose yes; `false` otherwise. + Attaching a handler to the element's `confirm` event that returns a `falsy` value cancels the confirmation dialog. + Attaching a handler to the element's `confirm:complete` event that returns a `falsy` value makes this function + return false. The `confirm:complete` event is fired whether or not the user answered true or false to the dialog. + */ + allowAction: function(element) { + var message = element.data('confirm'), + answer = false, callback; + if (!message) { return true; } - if (link.attr('data-remote') != undefined) { - handleRemote(link); + if (rails.fire(element, 'confirm')) { + answer = rails.confirm(message); + callback = rails.fire(element, 'confirm:complete', [answer]); + } + return answer && callback; + }, + + // Helper function which checks for blank inputs in a form that match the specified CSS selector + blankInputs: function(form, specifiedSelector, nonBlank) { + var inputs = $(), input, + selector = specifiedSelector || 'input,textarea'; + form.find(selector).each(function() { + input = $(this); + // Collect non-blank inputs if nonBlank option is true, otherwise, collect blank inputs + if (nonBlank ? input.val() : !input.val()) { + inputs = inputs.add(input); + } + }); + return inputs.length ? inputs : false; + }, + + // Helper function which checks for non-blank inputs in a form that match the specified CSS selector + nonBlankInputs: function(form, specifiedSelector) { + return rails.blankInputs(form, specifiedSelector, true); // true specifies nonBlank + }, + + // Helper function, needed to provide consistent behavior in IE + stopEverything: function(e) { + $(e.target).trigger('ujs:everythingStopped'); + e.stopImmediatePropagation(); return false; - } else if (link.attr('data-method')) { - handleMethod(link); + }, + + // find all the submit events directly bound to the form and + // manually invoke them. If anyone returns false then stop the loop + callFormSubmitBindings: function(form, event) { + var events = form.data('events'), continuePropagation = true; + if (events !== undefined && events['submit'] !== undefined) { + $.each(events['submit'], function(i, obj){ + if (typeof obj.handler === 'function') return continuePropagation = obj.handler(event); + }); + } + return continuePropagation; + }, + + // replace element's html with the 'data-disable-with' after storing original html + // and prevent clicking on it + disableElement: function(element) { + element.data('ujs:enable-with', element.html()); // store enabled state + element.html(element.data('disable-with')); // set to disabled state + element.bind('click.railsDisable', function(e) { // prevent further clicking + return rails.stopEverything(e) + }); + }, + + // restore element to its original state which was disabled by 'disableElement' above + enableElement: function(element) { + if (element.data('ujs:enable-with') !== undefined) { + element.html(element.data('ujs:enable-with')); // set to old enabled state + // this should be element.removeData('ujs:enable-with') + // but, there is currently a bug in jquery which makes hyphenated data attributes not get removed + element.data('ujs:enable-with', false); // clean up cache + } + element.unbind('click.railsDisable'); // enable element + } + + }; + + $.ajaxPrefilter(function(options, originalOptions, xhr){ if ( !options.crossDomain ) { rails.CSRFProtection(xhr); }}); + + $(document).delegate(rails.linkDisableSelector, 'ajax:complete', function() { + rails.enableElement($(this)); + }); + + $(document).delegate(rails.linkClickSelector, 'click.rails', function(e) { + var link = $(this), method = link.data('method'), data = link.data('params'); + if (!rails.allowAction(link)) return rails.stopEverything(e); + + if (link.is(rails.linkDisableSelector)) rails.disableElement(link); + + if (link.data('remote') !== undefined) { + if ( (e.metaKey || e.ctrlKey) && (!method || method === 'GET') && !data ) { return true; } + + if (rails.handleRemote(link) === false) { rails.enableElement(link); } + return false; + + } else if (link.data('method')) { + rails.handleMethod(link); return false; } }); - $('form').live('submit.rails', function(e) { - var form = $(this), remote = form.attr('data-remote') != undefined; - if (!allowAction(form)) return false; + $(document).delegate(rails.inputChangeSelector, 'change.rails', function(e) { + var link = $(this); + if (!rails.allowAction(link)) return rails.stopEverything(e); - // skip other logic when required values are missing - if (requiredValuesMissing(form)) return !remote; + rails.handleRemote(link); + return false; + }); + + $(document).delegate(rails.formSubmitSelector, 'submit.rails', function(e) { + var form = $(this), + remote = form.data('remote') !== undefined, + blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector), + nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector); + + if (!rails.allowAction(form)) return rails.stopEverything(e); + + // skip other logic when required values are missing or file upload is present + if (blankRequiredInputs && form.attr("novalidate") == undefined && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) { + return rails.stopEverything(e); + } if (remote) { - handleRemote(form); + if (nonBlankFileInputs) { + return rails.fire(form, 'ajax:aborted:file', [nonBlankFileInputs]); + } + + // If browser does not support submit bubbling, then this live-binding will be called before direct + // bindings. Therefore, we should directly call any direct bindings before remotely submitting form. + if (!$.support.submitBubbles && $().jquery < '1.7' && rails.callFormSubmitBindings(form, e) === false) return rails.stopEverything(e); + + rails.handleRemote(form); return false; + } else { // slight timeout so that the submit button gets properly serialized - setTimeout(function(){ disableFormElements(form) }, 13); + setTimeout(function(){ rails.disableFormElements(form); }, 13); } }); - $('form input[type=submit], form button[type=submit], form button:not([type])').live('click.rails', function() { + $(document).delegate(rails.formInputClickSelector, 'click.rails', function(event) { var button = $(this); - if (!allowAction(button)) return false; + + if (!rails.allowAction(button)) return rails.stopEverything(event); + // register the pressed submit button - var name = button.attr('name'), data = name ? {name:name, value:button.val()} : null; + var name = button.attr('name'), + data = name ? {name:name, value:button.val()} : null; + button.closest('form').data('ujs:submit-button', data); }); - $('form').live('ajax:beforeSend.rails', function(event) { - if (this == event.target) disableFormElements($(this)); + $(document).delegate(rails.formSubmitSelector, 'ajax:beforeSend.rails', function(event) { + if (this == event.target) rails.disableFormElements($(this)); }); - $('form').live('ajax:complete.rails', function(event) { - if (this == event.target) enableFormElements($(this)); + $(document).delegate(rails.formSubmitSelector, 'ajax:complete.rails', function(event) { + if (this == event.target) rails.enableFormElements($(this)); }); + })( jQuery ); - diff --git a/public/javascripts/vendor/jquery.iframe-transport.js b/public/javascripts/vendor/jquery.iframe-transport.js new file mode 100644 index 000000000..3407da0a5 --- /dev/null +++ b/public/javascripts/vendor/jquery.iframe-transport.js @@ -0,0 +1,233 @@ +// This [jQuery](http://jquery.com/) plugin implements an `"); + + // The first load event gets fired after the iframe has been injected + // into the DOM, and is used to prepare the actual submission. + iframe.bind("load", function() { + + // The second load event gets fired when the response to the form + // submission is received. The implementation detects whether the + // actual payload is embedded in a `