Skip to content

Instantly share code, notes, and snippets.

@ttilley
Created February 27, 2010 01:25
Show Gist options
  • Save ttilley/316376 to your computer and use it in GitHub Desktop.
Save ttilley/316376 to your computer and use it in GitHub Desktop.
dojo.provide('rails._util.ujs');
dojo.require('rails._util.xhrDomEvents');
dojo.require('dojo.NodeList-traverse');
// main handlers:
// form[data-remote] -> submit
// input[data-remote] -> click
// a[data-remote] -> click
// a[data-method]:not([data-remote]) -> click
//
// other important selectors:
// input[data-disable-with]
// form[data-remote]:has(input[data-disable-with])
//
// HTML5 data attributes:
// data-remote -> ajax-enable this element
// data-method -> HTTP verb emulation
// data-confirm -> add a confirmation dialog
// data-disable-with -> disable inputs/forms with in-flight requests
// data-url (deprecated, use href) -> define endpoint for verb emulation
//
// Cross-Site tags:
// meta[name=csrf-token]
// meta[name=csrf-param]
//
// data-remote request lifecycle events:
// ajax:before
// ajax:loading
// ajax:loaded
// ajax:interactive
// ajax:complete
// ajax:success
// ajax:failure
// ajax:after
(function(){
function ajaxEnable(el){
var method, url, params, hasBody;
if (el.tagName.toLowerCase() == 'form') {
method = el.attr('method') || 'post';
url = el.attr('action');
params = dojo.formToObject(el);
hasBody = true;
}
else {
method = el.attr('data-method') || 'get';
url = el.attr('data-url') || el.attr('href');
params = {};
hasBody = false;
}
dojo.xhr(method, {
url: url,
handleAs: 'javascript',
content: params,
domEvents: el
}, hasBody);
}
function emulateVerb(el){
var action = dojo.attr(el, 'data-url') || dojo.attr(el, 'href');
var emulatedVerb = dojo.attr(el, 'data-method');
var form = dojo.create('form', {
method: 'post',
action: action,
styles: {
display: 'none'
}
}, el, 'after');
if (emulatedVerb.toLowerCase() != 'post') {
dojo.create('input', {
type: 'hidden',
name: '_method',
value: emulatedVerb
}, form, 'last');
}
var csrf_param = dojo.query('meta[name=csrf-param]').attr('content')[0] || false;
var csrf_token = dojo.query('meta[name=csrf-token]').attr('content')[0] || false;
if (csrf_param && csrf_token) {
dojo.create('input', {
type: 'hidden',
name: csrf_param,
value: csrf_token
}, form, 'last');
}
form.submit();
}
function seekDisableableElement(el){
return dojo.hasAttr(el, 'data-disable-with') ? el : dojo.query('[data-disable-with]', el)[0];
}
function disableInput(element){
var el = seekDisableableElement(element);
if (!el) {
return true;
}
if (!dojo.hasAttr(el, 'data-enable-with')) {
dojo.attr(el, 'data-enable-with', dojo.attr(el, 'value'));
}
dojo.attr(el, {
value: dojo.attr(el, 'data-disable-with'),
disabled: true
});
}
function enableInput(el){
if (dojo.hasAttr(el, 'data-enable-with')) {
var value = dojo.attr(el, 'data-enable-with');
dojo.attr(el, 'value', value);
}
dojo.attr(el, 'disabled', false);
}
dojo.connect(document.body, 'onclick', function(event){
var message = dojo.attr(event.target, 'data-confirm');
if (message && !confirm(message)) {
dojo.stopEvent(event);
return false;
}
var wrapper = new dojo.NodeList(event.target);
var usesAjax = wrapper.closest("a[data-remote]")[0];
if (usesAjax) {
ajaxEnable(usesAjax);
dojo.stopEvent(event);
return true;
}
var usesVerbEmulation = wrapper.closest("a[data-method]")[0];
if (usesVerbEmulation) {
emulateVerb(usesVerbEmulation);
dojo.stopEvent(event);
return true;
}
});
dojo.connect(document.body, 'onsubmit', function(event){
var message = dojo.attr(event.target, 'data-confirm');
if (message && !confirm(message)) {
dojo.stopEvent(event);
return false;
}
var inputs = dojo.query("input[type=submit][data-disable-with]", event.target);
for (var input in inputs) {
disableInput(input);
}
var usesAjax = new dojo.NodeList(event.target).closest("form[data-remote]")[0];
if (usesAjax) {
ajaxEnable(usesAjax);
dojo.stopEvent(event);
return true;
}
});
dojo.connect(document.body, 'ajax:complete', function(event){
if (event.target.tagName.toLowerCase() == 'form') {
var inputs = dojo.query("input[type=submit][disabled=true][data-disable-with]", event.target);
for (var input in inputs) {
enableInput(input);
}
}
});
})();
dojo.provide('rails._util.xhrDomEvents');
dojo.require('rails._util.xhrReadyState');
dojo.require('plugd.trigger');
(function(){
var _xhr = dojo.xhr;
var _xhrDomEventHandler = function(element, customEvent){
var handler = dojo.hitch(element, dojo.trigger, element, customEvent);
return function(xhr){
handler({
request: xhr
});
};
};
var _xhrDomEventsFor = function(element){
return {
onLoading: _xhrDomEventHandler(element, 'ajax:loading'),
onLoaded: _xhrDomEventHandler(element, 'ajax:loaded'),
onInteractive: _xhrDomEventHandler(element, 'ajax:interactive'),
onComplete: _xhrDomEventHandler(element, 'ajax:complete')
};
};
dojo.xhr = function(method, args, hasBody){
if (args && args.domEvents) {
var element = args.domEvents;
delete args.domEvents;
var dfd;
if (!dojo.trigger(element, 'ajax:before')) {
var emptyFunction = function(){
};
dfd = dojo._ioSetArgs(args, emptyFunction, emptyFunction, emptyFunction);
dfd.cancel();
return dfd;
}
var readyStateCallbacks = _xhrDomEventsFor(element);
if (args.readyStateCallbacks) {
var overWrite = args.readyStateCallbacks;
dojo.mixin(readyStateCallbacks, overWrite);
}
args.readyStateCallbacks = readyStateCallbacks;
dfd = _xhr(method, args, hasBody);
dojo.trigger(element, 'ajax:after');
var ajaxSuccess = _xhrDomEventHandler(element, 'ajax:success');
var ajaxFailure = _xhrDomEventHandler(element, 'ajax:failure');
dfd.addCallbacks(function(dfd){
ajaxSuccess(dfd.ioArgs.xhr);
}, function(dfd){
ajaxFailure(dfd.ioArgs.xhr);
});
return dfd;
}
else {
return _xhr(method, args, hasBody);
}
};
})();
dojo.provide('rails._util.xhrReadyState');
(function(){
var _xhrObj = dojo._xhrObj;
var _readyStates = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
var _readyStateHandler = function(callbacks, event){
var xhr = event.target;
var readyState = xhr.readyState;
var state = String(_readyStates[readyState]);
var callback = callbacks['on' + state];
if (callback && dojo.isFunction(callback)) {
callback(xhr);
}
};
dojo._xhrObj = function(args){
var http = _xhrObj();
var callbacks = args.readyStateCallbacks;
if (callbacks) {
delete args.readyStateCallbacks;
try {
http.onreadystatechange = dojo.hitch(null, _readyStateHandler, callbacks);
}
catch (e) {
console.error("Unable to setup ready state handler:");
console.error(e);
}
}
return http;
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment