diff --git a/src/rails.js b/src/rails.js index 17c0b371..874ffd5b 100644 --- a/src/rails.js +++ b/src/rails.js @@ -28,58 +28,56 @@ // Triggers an event on an element and returns the event result function fire(obj, name, data) { - var event = new $.Event(name); + var event = $.Event(name); obj.trigger(event, data); return event.result !== false; } - // Submits "remote" forms and links with ajax - function handleRemote(element) { - var method, url, data, - dataType = element.attr('data-type') || ($.ajaxSettings && $.ajaxSettings.dataType); - - 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; - } - - $.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); - } - 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]); - } - }); + function handleRemoteLink(link, event) { + ajax(link, link.attr('href'), link.data('method'), null, event); } + function handleRemoteForm(form, event) { + if (form.find('input[required]').filter(isMissing).length == 0) { + var data = form.serializeArray(); + // memoized value from clicked submit button + var button = form.data('ujs:submit-button'); + if (button) { + data.push(button); + form.data('ujs:submit-button', null); + } + ajax(form, form.attr('action'), form.attr('method'), data, event); + } + event.preventDefault(); + } + + function trigger(element, name) { + return function() element.trigger('ajax:' + name, arguments); + } + + function ajax(element, url, method, data, event) { + var dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType); + $.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); + } + return fire(element, 'ajax:beforeSend', [xhr, settings]); + }, + success: trigger(element, 'success'), + complete: trigger(element, 'complete'), + error: trigger(element, 'error') + }); + event.preventDefault(); + } + // Handles "data-method" on links such as: // Delete - function handleMethod(link) { + function handleMethod(link, event) { var href = link.attr('href'), - method = link.attr('data-method'), + method = link.data('method'), csrf_token = $('meta[name=csrf-token]').attr('content'), csrf_param = $('meta[name=csrf-param]').attr('content'), form = $('
'), @@ -91,79 +89,61 @@ form.hide().append(metadata_input).appendTo('body'); form.submit(); + event.preventDefault(); } 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.find('input[data-disable-with]').attr('disabled', 'disabled').val(function(index, value) { + return $(this).data('ujs:enable-with', value).data('disable-with'); + }); } function enableFormElements(form) { - form.find('input[data-disable-with]').each(function() { - var input = $(this); - input.val(input.data('ujs:enable-with')).removeAttr('disabled'); - }); + form.find('input[data-disable-with]').removeAttr('disabled').val(function() { + return $(this).data('ujs:enable-with'); + }); } function allowAction(element) { - var message = element.attr('data-confirm'); + var message = element.data('confirm'); return !message || (fire(element, 'confirm') && confirm(message)); } - function requiredValuesMissing(form) { - var missing = false; - form.find('input[name][required]').each(function() { - if (!$(this).val()) missing = true; - }); - return missing; - } - - $('a[data-confirm], a[data-method], a[data-remote]').live('click.rails', function(e) { - var link = $(this); - if (!allowAction(link)) return false; - - if (link.attr('data-remote') != undefined) { - handleRemote(link); - return false; - } else if (link.attr('data-method')) { - handleMethod(link); - return false; - } - }); - - $('form').live('submit.rails', function(e) { - var form = $(this), remote = form.attr('data-remote') != undefined; - if (!allowAction(form)) return false; - - // skip other logic when required values are missing - if (requiredValuesMissing(form)) return !remote; - - if (remote) { - handleRemote(form); - return false; - } else { - // slight timeout so that the submit button gets properly serialized - setTimeout(function(){ disableFormElements(form) }, 13); - } - }); - - $('form input[type=submit], form button[type=submit], form button:not([type])').live('click.rails', function() { - var button = $(this); - if (!allowAction(button)) return false; - // register the pressed submit button - 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)); + function register(button) { + var name = button.attr('name'), data = name ? {name:name, value:button.val()} : null; + button.closest('form').data('ujs:submit-button', data); + } + + function isMissing() { + return !$(this).val(); + } + + $.fn.on = function(name, fn) { + return $(this).live(name + '.rails', fn); + } + + $.fn.ifTargetOn = function(name, fn) { + return $(this).on('ajax:' + name, function(event) { + if (this == event.target) fn($(this)); + }); + } + + $.fn.ifAllowedOn = function(name, fn) { + $(this).on(name, function(event) { + var element = $(this); + allowAction(element) ? fn(element, event) : event.preventDefault(); + }); + } + + $('a[data-remote]').ifAllowedOn('click', handleRemoteLink); + $('a[data-method]:not([data-remote])').ifAllowedOn('click', handleMethod); + $('form[data-remote]').ifAllowedOn('submit', handleRemoteForm); + + $('form:not([data-remote])').ifAllowedOn('submit', function(form, event) { + // slight timeout so that the submit button gets properly serialized + setTimeout(function(){ disableFormElements(form) }, 13); }); - $('form').live('ajax:complete.rails', function(event) { - if (this == event.target) enableFormElements($(this)); - }); + $('form input[type=submit], form button[type=submit], form button:not([type])').ifAllowedOn('click', register) + $('form').ifTargetOn('beforeSend', disableFormElements).ifTargetOn('complete', enableFormElements); })( jQuery );