Created
September 13, 2009 21:08
-
-
Save jnunemaker/186328 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
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