Skip to content

Instantly share code, notes, and snippets.

@jmarnold
Created June 19, 2012 03:42
Show Gist options
  • Save jmarnold/2952177 to your computer and use it in GitHub Desktop.
Save jmarnold/2952177 to your computer and use it in GitHub Desktop.
Ajax continuation js
// fubuvalidation.js v0.5.7
//
// Copyright (C)2011 Joshua Arnold
// Distributed under Apache License, Version 2.0
//
// https://github.com/DarthFubuMVC/fubuvalidation-js
(function ($, continuations) {
_.templateSettings = { interpolate: /\{\{(.+?)\}\}/g };
var handlers = [];
var finders = [];
var defaultHandler = function () { };
defaultHandler.prototype = {
matches: function (context) { return true; },
reset: function (context) {
var self = this;
context.container.find('.validation-summary').html('');
context.container.hide();
$('.error', context.form).each(function () {
self.unhighlight($(this));
});
},
process: function (context) {
var self = this;
var container = $('.validation-container', context.form);
context.container = container;
context.summary = container.find('ul.validation-summary');
this.reset(context);
if (context.errors.length == 0) {
return;
}
container.show();
$.fubuvalidation.eachError(context, function (error) {
self.append(context, error);
self.highlight(error);
});
},
append: function (context, error) {
var found = false;
context
.summary
.find("li[data-field='" + error.field + "']")
.each(function () {
if (found) return;
if ($(this).find('a').html() == error.message) {
found = true;
return;
}
});
if (!found) {
var self = this;
var token = $(_.template('<li data-field="{{ label }}"><a href="javascript:void(0);">{{ token }}</a></li>', {
field: error.field,
label: error.label,
token: self.generateToken(error)
}));
token.find('a').click(function () {
$.fubuvalidation.findElement(context, error.field).focus();
});
context.summary.append(token);
}
},
generateToken: function (error) {
return _.template('{{ label }} - {{ message }}', error);
},
highlight: function (error) {
if (error.element) {
$(error.element).addClass('error');
}
},
unhighlight: function (element) {
element.removeClass('error');
}
};
// This instance is registered by default and made public via $.fubuvalidation.defaultHandler
var theDefault = new defaultHandler();
var validation = function () { };
validation.prototype = {
init: function () {
this.setupDefaults();
},
// this is here for testing
reset: function () {
handlers.length = 0;
finders.length = 0;
this.setupDefaults();
},
setupDefaults: function () {
this.findElementsWith(function (searchContext) {
searchContext.element = $('#' + searchContext.key, searchContext.form);
});
this.registerHandler(theDefault);
},
registerHandler: function (handler) {
handlers.push(handler);
return this;
},
findHandler: function (context) {
var handler;
for (var i = 0; i < handlers.length; i++) {
var x = handlers[i];
if (x.matches(context)) {
handler = x;
}
}
return handler;
},
findElementsWith: function (finder) {
finders.push(finder);
return this;
},
findElement: function (context, key, error) {
var searchContext = {
key: key,
error: error,
form: context.form
};
for (var i = 0; i < finders.length; i++) {
var finder = finders[i];
finder(searchContext);
}
return searchContext.element;
},
toValidationContext: function (continuation) {
var self = this;
this.eachError(continuation, function (e) {
if (!e.element) {
e.element = self.findElement(continuation, e.field, e);
}
});
return continuation;
},
eachError: function (context, action) {
for (var i = 0; i < context.errors.length; i++) {
action(context.errors[i]);
}
},
process: function (continuation) {
var context = this.toValidationContext(continuation);
var handler = this.findHandler(context);
handler.process(context);
}
};
var module = new validation();
module.init();
$.fubuvalidation = module;
// export the class for extension
$.fubuvalidation.defaultHandlerClass = defaultHandler;
$.fubuvalidation.defaultHandler = theDefault;
var reset = $.fn.resetForm;
$.fn.resetForm = function () {
var context = {
form: $(this),
container: $('.validation-container', $(this))
};
$.fubuvalidation.defaultHandler.reset(context);
reset.call(this);
};
$.continuations.applyPolicy({
matches: function (continuation) {
return continuation.matchOnProperty('form', function(form) {
return form.size() != 0;
});
},
execute: function (continuation) {
if (!continuation.errors) {
continuation.errors = [];
}
$.fubuvalidation.process(continuation);
}
});
} (jQuery));
// jquery.continuations v0.3.8
//
// Copyright (C)2011 Joshua Arnold, Jeremy Miller
// Distributed Under Apache License, Version 2.0
//
// https://github.com/DarthFubuMVC/jquery-continuations
(function ($, aggregator) {
"use strict";
// Sanity check of dependencies
if (typeof ($) !== 'function') {
throw 'jQuery.continuations: jQuery not found.';
}
var CORRELATION_ID = 'X-Correlation-Id';
var policies = [];
var theContinuation = function () { };
theContinuation.prototype = {
success: false,
errors: [],
refresh: false,
correlationId: null,
options: {},
matchOnProperty: function(prop, predicate) {
return typeof(this[prop]) !== 'undefined' && predicate(this[prop]);
},
isCorrelated: function () {
return this.matchOnProperty('correlationId', function(id) {
return id != null;
});
}
};
var refreshPolicy = function () {
this.matches = function (continuation) {
return continuation.refresh && continuation.refresh.toString() === 'true';
};
this.execute = function (continuation) {
$.continuations.windowService.refresh();
};
};
var navigatePolicy = function () {
this.matches = function (continuation) {
return continuation.navigatePage != undefined && continuation.navigatePage != '';
};
this.execute = function (continuation) {
$.continuations.windowService.navigateTo(continuation.navigatePage);
};
};
var errorPolicy = function () {
this.matches = function (continuation) {
return continuation.errors && continuation.errors.length != 0;
};
this.execute = function (continuation) {
$.continuations.eventAggregator.publish('ContinuationError', continuation);
};
};
var payloadPolicy = function () {
this.matches = function (continuation) {
return continuation.topic != null && continuation.payload != null;
};
this.execute = function (continuation) {
$.continuations.eventAggregator.publish(continuation.topic, continuation.payload);
};
};
var continuations = function () { };
continuations.prototype = {
init: function () {
$(document).ajaxComplete(function (e, xhr, options) {
$.continuations.eventAggregator.publish('AjaxCompleted', {
correlationId: xhr.getResponseHeader(CORRELATION_ID)
});
});
var self = this;
$.ajaxSetup({
cache: false,
success: function (continuation, status, jqXHR) {
var options = this.options;
if(typeof(options) === 'undefined') {
options = {};
}
if(typeof(continuation) !== 'undefined') {
continuation.options = options;
}
self.onSuccess({
continuation: continuation,
callback: this.continuationSuccess,
status: status,
response: jqXHR
});
},
beforeSend: function (xhr, settings) {
self.setupRequest(xhr, settings);
}
});
this.setupDefaults();
},
setupDefaults: function () {
this.applyPolicy(new refreshPolicy());
this.applyPolicy(new navigatePolicy());
this.applyPolicy(new errorPolicy());
this.applyPolicy(new payloadPolicy());
},
onSuccess: function (msg) {
var contentType = msg.response.getResponseHeader('Content-Type');
if (!contentType || contentType.indexOf('json') == -1) {
return;
}
var continuation = msg.continuation;
continuation.correlationId = msg.response.getResponseHeader('X-Correlation-Id');
if($.isFunction(msg.callback)) {
msg.callback(continuation);
}
this.process(continuation);
},
// Keep this public for form correlation
setupRequest: function (xhr, settings) {
// this could come from the ajax options
var id = settings.correlationId;
if (typeof(id) === 'undefined') {
id = new Date().getTime().toString();
}
xhr.setRequestHeader(CORRELATION_ID, id);
$.continuations.eventAggregator.publish('AjaxStarted', {
correlationId: id
});
},
applyPolicy: function (policy) {
policies.push(policy);
return this;
},
// Mostly for testing
reset: function() {
policies.length = 0;
this.setupDefaults();
},
process: function (continuation) {
var standardContinuation = new $.continuations.continuation();
continuation = $.extend(standardContinuation, continuation);
var matchingPolicies = [];
for (var i = 0; i < policies.length; ++i) {
var p = policies[i];
if (p.matches(continuation)) {
matchingPolicies.push(p);
}
}
for (var i = 0; i < matchingPolicies.length; ++i) {
matchingPolicies[i].execute(continuation);
}
},
useAmplify: function () {
$.continuations.eventAggregator = amplify;
}
};
continuations.prototype.windowService = {
refresh: function () {
window.location.reload();
},
navigateTo: function (url) {
window.location = url;
}
};
continuations.prototype.eventAggregator = {
publish: function (topic, payload) {
// no-op
},
subscribe: function(topic, context, callback) {
// no-op
}
};
var module = new continuations();
module.init();
// Make it global
$.continuations = module;
$.continuations.useAmplify();
$.continuations.continuation = theContinuation;
$.fn.correlatedSubmit = function (options) {
if(typeof(options) === 'undefined') {
options = {};
}
return this.each(function () {
var self = $(this);
var correlationId = options.correlationId;
if (typeof(correlationId) === 'undefined') {
var id = self.attr('id');
if (!id) {
id = 'form_' + new Date().getTime().toString();
self.attr('id', id);
}
correlationId = id;
}
self.ajaxSubmit({
correlationId: correlationId,
continuationSuccess: function(continuation) {
continuation.form = self;
continuation.options = options;
if($.isFunction(options.continuationSuccess)) {
options.continuationSuccess(continuation);
}
}
});
});
};
} (jQuery));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment