Skip to content

Commit

Permalink
Gave external api to internal rails.js functions to make them available
Browse files Browse the repository at this point in the history
to be called and modified from outside of rails.js via $.rails object.
Closes #98.
  • Loading branch information
JangoSteve authored and Neeraj Singh committed Apr 17, 2011
1 parent d2abd6f commit d591441
Showing 1 changed file with 48 additions and 42 deletions.
90 changes: 48 additions & 42 deletions src/rails.js
Expand Up @@ -36,30 +36,29 @@
*/ */


(function($) { (function($) {
// Shorthand to make it a little easier to call public rails functions from within rails.js
var rails;

$.rails = rails = {
// Make sure that every Ajax request sends the CSRF token // Make sure that every Ajax request sends the CSRF token
function CSRFProtection(xhr) { CSRFProtection: function(xhr) {
var token = $('meta[name="csrf-token"]').attr('content'); var token = $('meta[name="csrf-token"]').attr('content');
if (token) xhr.setRequestHeader('X-CSRF-Token', token); if (token) xhr.setRequestHeader('X-CSRF-Token', token);
} },
if ('ajaxPrefilter' in $) {
$.ajaxPrefilter(function(options, originalOptions, xhr){ CSRFProtection(xhr); });
} else {
$(document).ajaxSend(function(e, xhr){ CSRFProtection(xhr); });
}


// Triggers an event on an element and returns the event result // Triggers an event on an element and returns the event result
function fire(obj, name, data) { fire: function(obj, name, data) {
var event = $.Event(name); var event = $.Event(name);
obj.trigger(event, data); obj.trigger(event, data);
return event.result !== false; return event.result !== false;
} },


// Submits "remote" forms and links with ajax // Submits "remote" forms and links with ajax
function handleRemote(element) { handleRemote: function(element) {
var method, url, data, var method, url, data,
dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType); dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType);


if (fire(element, 'ajax:before')) { if (rails.fire(element, 'ajax:before')) {
if (element.is('form')) { if (element.is('form')) {
method = element.attr('method'); method = element.attr('method');
url = element.attr('action'); url = element.attr('action');
Expand All @@ -82,7 +81,7 @@
if (settings.dataType === undefined) { if (settings.dataType === undefined) {
xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script); xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script);
} }
return fire(element, 'ajax:beforeSend', [xhr, settings]); return rails.fire(element, 'ajax:beforeSend', [xhr, settings]);
}, },
success: function(data, status, xhr) { success: function(data, status, xhr) {
element.trigger('ajax:success', [data, status, xhr]); element.trigger('ajax:success', [data, status, xhr]);
Expand All @@ -95,11 +94,11 @@
} }
}); });
} }
} },


// Handles "data-method" on links such as: // Handles "data-method" on links such as:
// <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a> // <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
function handleMethod(link) { handleMethod: function(link) {
var href = link.attr('href'), var href = link.attr('href'),
method = link.data('method'), method = link.data('method'),
csrf_token = $('meta[name=csrf-token]').attr('content'), csrf_token = $('meta[name=csrf-token]').attr('content'),
Expand All @@ -113,54 +112,54 @@


form.hide().append(metadata_input).appendTo('body'); form.hide().append(metadata_input).appendTo('body');
form.submit(); form.submit();
} },


function disableFormElements(form) { disableFormElements: function(form) {
form.find('input[data-disable-with], button[data-disable-with]').each(function() { form.find('input[data-disable-with], button[data-disable-with]').each(function() {
var element = $(this), method = element.is('button') ? 'html' : 'val'; var element = $(this), method = element.is('button') ? 'html' : 'val';
element.data('ujs:enable-with', element[method]()); element.data('ujs:enable-with', element[method]());
element[method](element.data('disable-with')); element[method](element.data('disable-with'));
element.attr('disabled', 'disabled'); element.attr('disabled', 'disabled');
}); });
} },


function enableFormElements(form) { enableFormElements: function(form) {
form.find('input[data-disable-with]:disabled, button[data-disable-with]:disabled').each(function() { form.find('input[data-disable-with]:disabled, button[data-disable-with]:disabled').each(function() {
var element = $(this), method = element.is('button') ? 'html' : 'val'; var element = $(this), method = element.is('button') ? 'html' : 'val';
if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with')); if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with'));
element.removeAttr('disabled'); element.removeAttr('disabled');
}); });
} },


function allowAction(element) { allowAction: function(element) {
var message = element.data('confirm'); var message = element.data('confirm');
return !message || (fire(element, 'confirm') && confirm(message)); return !message || (rails.fire(element, 'confirm') && confirm(message));
} },


function blankInputs(form, specifiedSelector) { blankInputs: function(form, specifiedSelector) {
var blankExists = false, var blankExists = false,
selector = specifiedSelector || 'input'; selector = specifiedSelector || 'input';
form.find(selector).each(function() { form.find(selector).each(function() {
if (!$(this).val()) blankExists = true; if (!$(this).val()) blankExists = true;
}); });
return blankExists; return blankExists;
} },


function nonBlankInputs(form, specifiedSelector) { nonBlankInputs: function(form, specifiedSelector) {
var nonBlankExists = false, var nonBlankExists = false,
selector = specifiedSelector || 'input'; selector = specifiedSelector || 'input';
form.find(selector).each(function() { form.find(selector).each(function() {
if ($(this).val()) nonBlankExists = true; if ($(this).val()) nonBlankExists = true;
}); });
return nonBlankExists; return nonBlankExists;
} },


function stopEverything(e) { stopEverything: function(e) {
e.stopImmediatePropagation(); e.stopImmediatePropagation();
return false; return false;
} },


function callFormSubmitBindings(form) { callFormSubmitBindings: function(form) {
var events = form.data('events'), continuePropagation = true; var events = form.data('events'), continuePropagation = true;
if (events != undefined && events['submit'] != undefined) { if (events != undefined && events['submit'] != undefined) {
$.each(events['submit'], function(i, obj){ $.each(events['submit'], function(i, obj){
Expand All @@ -169,58 +168,65 @@
} }
return continuePropagation; return continuePropagation;
} }
};

if ('ajaxPrefilter' in $) {
$.ajaxPrefilter(function(options, originalOptions, xhr){ rails.CSRFProtection(xhr); });
} else {
$(document).ajaxSend(function(e, xhr){ rails.CSRFProtection(xhr); });
}


$('a[data-confirm], a[data-method], a[data-remote]').live('click.rails', function(e) { $('a[data-confirm], a[data-method], a[data-remote]').live('click.rails', function(e) {
var link = $(this); var link = $(this);
if (!allowAction(link)) return stopEverything(e); if (!rails.allowAction(link)) return rails.stopEverything(e);


if (link.data('remote') != undefined) { if (link.data('remote') != undefined) {
handleRemote(link); rails.handleRemote(link);
return false; return false;
} else if (link.data('method')) { } else if (link.data('method')) {
handleMethod(link); rails.handleMethod(link);
return false; return false;
} }
}); });


$('form').live('submit.rails', function(e) { $('form').live('submit.rails', function(e) {
var form = $(this), remote = form.data('remote') != undefined; var form = $(this), remote = form.data('remote') != undefined;
if (!allowAction(form)) return stopEverything(e); if (!rails.allowAction(form)) return rails.stopEverything(e);


// skip other logic when required values are missing or file upload is present // skip other logic when required values are missing or file upload is present
if (blankInputs(form, 'input[name][required]') && fire(form, 'ajax:aborted:required')) { if (rails.blankInputs(form, 'input[name][required]') && rails.fire(form, 'ajax:aborted:required')) {
return !remote; return !remote;
} }
if (nonBlankInputs(form, 'input:file')) { if (rails.nonBlankInputs(form, 'input:file')) {
return fire(form, 'ajax:aborted:file'); return rails.fire(form, 'ajax:aborted:file');
} }


// If browser does not support submit bubbling, then this live-binding will be called before direct // 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. // bindings. Therefore, we should directly call any direct bindings before remotely submitting form.
if (!$.support.submitBubbles && callFormSubmitBindings(form) == false) return stopEverything(e) if (!$.support.submitBubbles && rails.callFormSubmitBindings(form) == false) return rails.stopEverything(e)


if (remote) { if (remote) {
handleRemote(form); rails.handleRemote(form);
return false; return false;
} else { } else {
// slight timeout so that the submit button gets properly serialized // 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 input[type=image], form button[type=submit], form button:not([type])').live('click.rails', function() { $('form input[type=submit], form input[type=image], form button[type=submit], form button:not([type])').live('click.rails', function() {
var button = $(this); var button = $(this);
if (!allowAction(button)) return stopEverything(e); if (!rails.allowAction(button)) return rails.stopEverything(e);
// register the pressed submit button // 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); button.closest('form').data('ujs:submit-button', data);
}); });


$('form').live('ajax:beforeSend.rails', function(event) { $('form').live('ajax:beforeSend.rails', function(event) {
if (this == event.target) disableFormElements($(this)); if (this == event.target) rails.disableFormElements($(this));
}); });


$('form').live('ajax:complete.rails', function(event) { $('form').live('ajax:complete.rails', function(event) {
if (this == event.target) enableFormElements($(this)); if (this == event.target) rails.enableFormElements($(this));
}); });
})( jQuery ); })( jQuery );

0 comments on commit d591441

Please sign in to comment.