Skip to content

Instantly share code, notes, and snippets.

@boffbowsh
Forked from jnunemaker/jquery.peoplebase.js
Created March 23, 2010 20:24
Show Gist options
  • Save boffbowsh/341601 to your computer and use it in GitHub Desktop.
Save boffbowsh/341601 to your computer and use it in GitHub Desktop.
/**
IMPORTANT: Requires this version of jquery
until 1.3.3 comes out http://gist.github.com/186325
ALSO: This is very dirty still and has not been
abstracted for use. It is just solving our immediate problems.
Use cases that must pass (and should be tested someday):
* Clicking on links updates layout
* Click around a bit and then use back/forward buttons
* Make sure recursive loading is working (#/sections/23)
* Forms should send beforeSubmit, beforeSend, success, error, valid
and invalid events
* Safari should send connection close url to ajaxSubmit for file uploads
* Should fire events for page loading, progress for each step and loaded
*/
(function ($) {
// errors is an array of errors
// render :json => {:errors => @item.errors.full_messages}
function FormErrors(errors) {
var error_count = errors.length;
function errorUl() {
var lis = '';
errors.forEach(function(error) {
lis += '<li>' + error + '</li>';
});
return '<ul>' + lis + '</ul>';
}
function errorHeading() {
var error_str = error_count === 1 ? 'error' : 'errors';
return '<h2>' + error_count + ' ' + error_str + ' prevented this form from being saved</h2>';
}
this.html = function () {
var html = '';
html += '<div class="errorExplanation" id="errorExplanation">';
html += errorHeading();
html += errorUl();
html += '</div>';
return html;
};
}
$.fn.removeErrors = function () {
return this.each(function () {
$(this).find('.errorExplanation').remove();
});
};
$.fn.showErrors = function (errors) {
return this.each(function () {
$(this).removeErrors().prepend(new FormErrors(errors).html());
});
};
})(jQuery);
var Layout = {
live_path_regex: {'loading':[], 'success':[]},
init: function() {
jQuery(function($) {
var hash = window.location.hash,
needs_default_hash = !hash || (hash && hash == '#/');
if (needs_default_hash) {
window.location.hash = '#/dashboard';
}
window.currentHash = window.location.hash;
Layout.handlePageLoad();
Layout.addObservers();
Layout.makeBackFowardButtonsWork();
});
},
destroy: function(url, options) {
Layout.post(url, {'_method':'delete'}, options);
},
post: function(url, data, options) {
$.ajax({
type : 'post',
dataType : 'json',
url : url,
data : data,
success : function(json) {
Layout.onSuccess(json);
if (options && options.success) {
options.success(json);
}
}
});
},
addObservers: function() {
Layout.observeHashChange();
Layout.observeLinks();
Layout.observeForms();
Layout.observeLivePath();
},
observeLinks: function() {
$('a.remote_destroy').live('click', function(event) {
event.preventDefault();
var $link = $(this);
confirmDialog('Are you sure you want to delete this?', {
ok: function() {
var callbacks = {
success: function(json) {
$link.trigger('destroy:success', [json]);
}
}
// url should work with or without # on href
var url = $link.attr('href').replace(/^#/, '');
Layout.destroy(url, callbacks);
}
});
return false;
});
$("a[href^='#/']").live('click', function(event) {
// allow command or ctrl + click to open in new tab
var ctrl_cmd = event.ctrlKey || event.metaKey;
if (!ctrl_cmd) {
event.preventDefault();
var $link = $(this);
Layout.updateHashWithoutLoad($link.attr('href'));
$(document).trigger('hashchange');
}
});
},
observeForms: function() {
$(document).bind('layout:success', function() {
$('form').removeErrors();
});
$('form').live('submit', function(event) {
event.preventDefault();
var $form = $(this);
$form.ajaxSubmit({
dataType: 'json',
data: {iframe: 1},
closeKeepAlive: $.browser.safari ? '/close_connection' : false,
beforeSubmit: function(data, form, options) {
$form.trigger('form:beforeSubmit', [data, form, options]);
},
beforeSend: function() {
$form.trigger('form:beforeSend');
},
success: function(json) {
if (json.errors) {
$form.showErrors(json.errors);
} else {
Layout.onSuccess(json);
$form.resetForm();
}
},
error: function(response, status, error) {
$form.trigger('form:error', [response, status, error]);
},
complete: function() {
$form.trigger('form:complete');
}
});
return false;
});
},
observeHashChange: function() {
$(document).bind('hashchange', Layout.reload);
},
updateHashWithoutLoad: function(location) {
window.currentHash = window.location.hash = location;
},
makeBackFowardButtonsWork: function() {
setInterval(function() {
var hash_is_new = window.location.hash && window.currentHash != window.location.hash;
if (hash_is_new) {
window.currentHash = window.location.hash;
Layout.handlePageLoad();
}
}, 300);
},
// Options are success and complete callbacks
load: function(path, options) {
path = path.replace(/^#/, '');
$(document).trigger('path:loading', [path]);
$(document).trigger('path:loading:' + path);
$.ajax({
url: path,
dataType: 'json',
success: function(json) {
Layout.onSuccess(json);
$(document).trigger('path:success', [path, json]);
$(document).trigger('path:success:' + path, [json]);
if (options && options.success) {
options.success();
}
},
complete: function() {
if (options && options.complete) {
options.complete();
}
}
});
},
// See Layout.load for options
reload: function(options) {
Layout.load(window.location.hash, options);
},
livePath: function(event, path, callback) {
if (typeof(test) == 'string') {
$(document).bind('path:' + event + ':' + path, callback);
} else {
Layout.live_path_regex[event].push([path, callback]);
}
},
observeLivePath: function() {
$(document).bind('path:loading', function(event, path) {
$(Layout.live_path_regex['loading']).each(function() {
if (matches = path.match(this[0])) {
this[1](matches);
}
});
});
$(document).bind('path:success', function(event, path, json) {
$(Layout.live_path_regex['success']).each(function() {
if (matches = path.match(this[0])) {
this[1](matches, json);
}
});
});
},
onSuccess: function(json) {
Layout.applyJSON(json);
$(document).trigger('layout:success');
},
applyJSON: function(json) {
for(action in json) {
var selectors = json[action];
switch(action) {
case 'replace' : for(selector in selectors) $(selector).html(selectors[selector]); break;
case 'append' : for(selector in selectors) $(selector).append(selectors[selector]); break;
case 'prepend' : for(selector in selectors) $(selector).prepend(selectors[selector]); break;
case 'replaceWith' : for(selector in selectors) $(selector).replaceWith(selectors[selector]); break;
case 'remove' : $(selectors.join(',')).remove(); break;
}
}
},
handlePageLoad: function() {
var segments = window.location.hash.replace(/^#\//, '').split('/'),
total = segments.length,
path = '';
$(document).trigger('page:loading');
function loadSectionsInOrder() {
var segment = segments.shift();
path += '/' + segment;
var onComplete = function() {
var loaded = total - segments.length,
finished = loaded == total;
$(document).trigger('page:progress', [total, loaded]);
if (finished) {
$(document).trigger('page:loaded');
} else {
loadSectionsInOrder();
}
};
Layout.load(path, {complete: onComplete});
}
// start the recursive loading of sections
loadSectionsInOrder();
}
};
Layout.init();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment