Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 84 additions & 104 deletions src/rails.js
Original file line number Diff line number Diff line change
Expand Up @@ -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:
// <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
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 = $('<form method="post" action="' + href + '"></form>'),
Expand All @@ -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 );