[Jifty-commit] r5761 - jifty/trunk/share/web/static/js
Jifty commits
jifty-commit at lists.jifty.org
Tue Aug 19 22:40:14 EDT 2008
Author: sterling
Date: Tue Aug 19 22:40:13 2008
New Revision: 5761
Modified:
jifty/trunk/share/web/static/js/jifty.js
Log:
Tidied Jifty.update() and improved some of the inline documentation.
Modified: jifty/trunk/share/web/static/js/jifty.js
==============================================================================
--- jifty/trunk/share/web/static/js/jifty.js (original)
+++ jifty/trunk/share/web/static/js/jifty.js Tue Aug 19 22:40:13 2008
@@ -1084,7 +1084,9 @@
}
}
-// Update a region. Takes a hash of named parameters, including:
+// Update a region. It takes two arguments.
+//
+// The first argument is a hash of named parameters, including:
// - 'actions' is an array of monikers to submit
// - 'action_arguments' is a hash of action monikers to hashes of arguments which should override any arguments coming from form fields
// the hash keys for 'action_arguments' are the values of the 'actions' array
@@ -1095,15 +1097,21 @@
// - 'path' is the path of the fragment (if this is a new fragment)
// - 'element' is the CSS selector of the element to update, if 'region' isn't supplied
// - 'mode' is one of 'Replace', 'Top', 'Bottom', 'Before', or 'After'
-// - 'effect' is the name of a Prototype Effect
+// - 'effect' is the name of an effect
+//
+// The second argument is the element (usually a submit button) that triggered
+// it.
+//
Jifty.update = function () {
- // loads
+ // Let the regular form submit take over if this browser can't do this
if (!Jifty.hasAjaxTransport) return true;
+
// XXX: prevent default behavior in IE
if(window.event) {
window.event.returnValue = false;
}
+ // Load the arguments
var named_args = arguments[0];
var trigger = arguments[1];
@@ -1119,55 +1127,107 @@
// Grab extra arguments (from a button)
var button_args = Jifty.Form.Element.buttonFormElements(trigger);
+ // Load the form to which this trigger element belongs
var form = Jifty.Form.Element.getForm(trigger);
+
// If the action is null, take all actions
if (named_args['actions'] == null) {
named_args['actions'] = {};
- // default to disable fields
- if (form)
+
+ // Add all the actions into the list to submit
+ if (form) {
jQuery.each(Jifty.Form.getActions(form), function(){
named_args['actions'][this.moniker] = 1;
});
+ }
}
+
+ // SPA = Special code related to the Single Page Plugin
+
+ // SPA Variable used to tell us to update __page under some conditions
var optional_fragments;
- if (form && form['J:CALL'])
- optional_fragments = [ prepare_element_for_update({'mode':'Replace','args':{},'region':'__page','path': null}) ];
- // Build actions structure
+
+ // SPA Update __page when the form calls a continuation
+ if (form && form['J:CALL']) {
+ optional_fragments = [
+ prepare_element_for_update({
+ 'mode': 'Replace',
+ 'args': {},
+ 'region': '__page',
+ 'path': null
+ })
+ ];
+ }
+
+ // Build actions request structure
var has_request = 0;
request['actions'] = {};
+ // Go through the monikers and actions we know about
for (var moniker in named_args['actions']) {
+
+ // SPA The extend moniker is special, skip it
if (moniker == 'extend')
continue;
+
+ // Remember this action, we will disable it in a minute
var disable = named_args['actions'][moniker];
+
+ // Find the information related to this action
var a = new Action(moniker, button_args);
+
+ // Stuff this into the current actions map
current_actions[moniker] = a;
- // Special case for Redirect, allow optional, implicit __page
+
+ // SPA Special case for Redirect, allow optional, implicit __page
// from the response to be used.
if (a.actionClass == 'Jifty::Action::Redirect') {
(function() {
var fields = a.fields();
var path = fields[fields.length - 1];
- optional_fragments = [ prepare_element_for_update({'mode':'Replace','args':{},'region':'__page','path': path}) ];
+ optional_fragments = [
+ prepare_element_for_update({
+ 'mode': 'Replace',
+ 'args': {},
+ 'region': '__page',
+ 'path': path
+ })
+ ];
})()
}
+
+ // Fill these with empty values for the moment
a.result = {};
a.result.field_error = {};
+ // Do we have an action registration field?
if (a.register) {
+
+ // Do we need to worry about a file upload field? If so, we cannot
+ // ajax this, do a full form submission.
+ //
+ // TODO Consider some IFRAME magic to fallback upon?
if (a.hasUpload()) {
+
// XXX: restore default behavior in IE (and Opera, Safari)
if(window.event) {
window.event.returnValue = true;
}
+
return true;
}
+
+ // Disable the action being submitted
if(disable) {
a.disable_input_fields(disabled_elements);
}
+
+ // Build a list of parameters
var param = a.data_structure();
var fields = param.fields;
var override = named_args['action_arguments'][param.moniker] || {};
+
+ // Override the action fields with action_arguments
for (var argname in override) {
if (fields[argname]) {
fields[argname].value = override[argname];
@@ -1176,131 +1236,192 @@
fields[argname] = { value: override[argname] };
}
}
+
+ // Add the parameters to the request we're building
request['actions'][moniker] = param;
+
+ // Remember that we have a request if we're submitting an action
++has_request;
}
}
+ // Get ready to specify the fragment updates we're looking for
request.fragments = {};
+
+ // CST - Client-side Templating
+
+ // CST Holding tank for client-cacheable templates
var update_from_cache = new Array;
- // Build fragments structure
+ // Build the fragments request
for (var i = 0; i < named_args['fragments'].length; i++) {
+
+ // Grab the current fragment
var f = named_args['fragments'][i];
+
+ // Put together the data structure that will request the fragment
f = prepare_element_for_update(f);
+
+ // Skip it if we just deleted the fragment
if (!f) continue;
+ // CST Load the fragment from cache if it has been saved there
var cached = CACHE[f['path']];
+
+ // CST XXX XXX XXX
if (cached && cached['type'] == 'static') {
var my_fragment = document.createElement('fragment');
var content_node = document.createElement('content');
var cached_result;
Jifty.Web.current_region = fragments[ f['region'] ];
- try { cached_result = apply_cached_for_action(cached['content'], []) }
+ try {
+ cached_result = apply_cached_for_action(cached['content'], [])
+ }
catch (e) { alert(e) }
content_node.textContent = cached_result;
my_fragment.appendChild(content_node);
my_fragment.setAttribute('id', f['region']);
- update_from_cache.push(function(){ apply_fragment_updates(my_fragment, f);
- } );
+ update_from_cache.push(function(){
+ apply_fragment_updates(my_fragment, f);
+ } );
+
continue;
}
+
+ // CST XXX XXX XXX
else if (cached && cached['type'] == 'action') {
var my_fragment = document.createElement('fragment');
var content_node = document.createElement('content');
my_fragment.appendChild(content_node);
my_fragment.setAttribute('id', f['region']);
- update_from_cache.push(function(){
- var cached_result;
- Jifty.Web.current_region = fragments[ f['region'] ];
- try {
- cached_result = apply_cached_for_action(cached['content'], Jifty.Form.getActions(form));
- }
- catch (e) { alert(e); throw e }
- content_node.textContent = cached_result;
- apply_fragment_updates(my_fragment, f);
- } );
+ update_from_cache.push(function() {
+ var cached_result;
+ Jifty.Web.current_region = fragments[ f['region'] ];
+ try {
+ cached_result = apply_cached_for_action(
+ cached['content'], Jifty.Form.getActions(form)
+ );
+ }
+ catch (e) { alert(e); throw e }
+ content_node.textContent = cached_result;
+ apply_fragment_updates(my_fragment, f);
+ } );
+
continue;
}
+
+ // CST XXX XXX XXX
else if (cached && cached['type'] == 'crudview') {
- try { // XXX: get model class etc as metadata in cache
+ try {
+ // XXX: get model class etc as metadata in cache
// XXX: kill dup code
- var Todo = new AsynapseRecord('todo');
- var record = Todo.find(f['args']['id']);
- var my_fragment = document.createElement('fragment');
- var content_node = document.createElement('content');
- content_node.textContent = cached['content'](record);
- my_fragment.appendChild(content_node);
- my_fragment.setAttribute('id', f['region']);
- update_from_cache.push(function(){ apply_fragment_updates(my_fragment, f); } );
- } catch (e) { alert(e) };
+ var Todo = new AsynapseRecord('todo');
+ var record = Todo.find(f['args']['id']);
+ var my_fragment = document.createElement('fragment');
+ var content_node = document.createElement('content');
+ content_node.textContent = cached['content'](record);
+ my_fragment.appendChild(content_node);
+ my_fragment.setAttribute('id', f['region']);
+ update_from_cache.push(function(){ apply_fragment_updates(my_fragment, f); } );
+ }
+ catch (e) { alert(e) };
+
continue;
}
- // Update with all new values
+ // Build a fragment request from the path and args
var name = f['region'];
- var fragment_request = fragments[ name ].data_structure(f['path'], f['args']);
+ var fragment_request = fragments[ name ].data_structure(
+ f['path'], f['args']
+ );
- if (f['is_new'])
- // Ask for the wrapper if we are making a new region
+ // Ask for the wrapper if we are making a new region
+ if (f['is_new']) {
fragment_request['wrapper'] = 1;
+ }
- if (fragments[name].in_form)
+ // Is the fragment in a form? Prevent <form></form> tags
+ if (fragments[name].in_form) {
fragment_request['in_form'] = 1;
+ }
// Push it onto the request stack
request.fragments[name] = fragment_request;
+
+ // Remember that we have a request if we're updating a fragment
++has_request;
}
+ // CST XXX XXX XXX
if (!has_request) {
- for (var i = 0; i < update_from_cache.length; i++)
+ for (var i = 0; i < update_from_cache.length; i++) {
update_from_cache[i]();
+ }
return false;
}
+ // Show the "Loading..." message (or equivalent)
show_wait_message();
- // And when we get the result back..
+ // And when we get the result back, we'll want to deal with it
+ //
+ // NOTE: Success here doesn't mean the server liked the request, but that
+ // the HTTP communication succeeded. There still might be errors validating
+ // fields, with the app connecting to the database, etc.
var onSuccess = function(responseXML, object) {
+
// Grab the XML response
var response = responseXML.documentElement;
- // Get action results
- walk_node(response,
- { result: function(result) {
+ // Look through the action results looking for field validation errors
+ walk_node(response, {
+ result: function(result) {
var moniker = result.getAttribute("moniker");
- walk_node(result,
- { field: function(field) {
- var error = field.getElementsByTagName('error')[0];
- if (error) {
- var text = error.textContent
- ? error.textContent
- : (error.firstChild ? error.firstChild.nodeValue : '');
- var action = current_actions[moniker];
- action.result.field_error[field.getAttribute("name")] = text;
- }
- }});
- }});
+ walk_node(result, {
+ field: function(field) {
+ var error = field.getElementsByTagName('error')[0];
+
+ // Record the validation errors and such with the form
+ if (error) {
+ var text
+ = error.textContent ? error.textContent
+ : (error.firstChild ? error.firstChild.nodeValue
+ : '');
+ var action = current_actions[moniker];
+ action.result
+ .field_error[field.getAttribute("name")] = text;
+ }
+ }
+ });
+ }
+ });
+ // Re-enable all the controls in the actions we previously disabled
for ( var i = 0; i < disabled_elements.length; i++ ) {
disabled_elements[i].disabled = false;
}
- // empty known action. XXX: we should only need to discard actions being submitted
+ // empty known action.
+ // XXX: we should only need to discard actions being submitted
+
+ // SPA We only care about __page sometimes
+ var expected_fragments = optional_fragments ? optional_fragments
+ : named_args['fragments'];
- // Loop through the result looking for it
- var expected_fragments = optional_fragments ? optional_fragments : named_args['fragments'];
+ // Loop through the response looking for fragments we requested
for (var response_fragment = response.firstChild;
- response_fragment != null && response_fragment.nodeName == 'fragment';
- response_fragment = response_fragment.nextSibling) {
+ response_fragment != null &&
+ response_fragment.nodeName == 'fragment';
+ response_fragment = response_fragment.nextSibling) {
+ // Get the returned ID attached to the new fragment for validation
var exp_id = response_fragment.getAttribute("id");
+ // Pull out the expected fragment from args matching the response
var f;
jQuery.each(expected_fragments, function() {
if (exp_id == this['region']) {
@@ -1308,56 +1429,77 @@
return false;
}
});
- if (!f)
+
+ // If we didn't expect it, skip it
+ if (!f) {
continue;
+ }
+ // Apply the fragment update to the page
try {
apply_fragment_updates(response_fragment, f);
- }catch (e) { alert(e) }
+ } catch (e) { alert(e) }
+
+ // CST XXX XXX XXX
extract_cacheable(response_fragment, f);
}
+ // CST XXX XXX XXX
jQuery.each(update_from_cache, function() {
this();
});
// update_from_cache.each(function(x) { x() });
- walk_node(response,
- { result: function(result) {
+ // Look through the response again
+ walk_node(response, {
+
+ // Report all the action results we have
+ result: function(result) {
for (var key = result.firstChild;
- key != null;
- key = key.nextSibling) {
+ key != null;
+ key = key.nextSibling) {
show_action_result(result.getAttribute("moniker"),key);
}
},
- redirect: function(redirect) {
+
+ // If we've been told to redirect, do it
+ redirect: function(redirect) {
document.location = redirect.firstChild.firstChild.nodeValue;
- }});
+ }
+ });
+
+ // Forget the actions, we're oh-fficially done with them
current_actions = {}
};
+ // When an HTTP communication failure happens, we need to clean up
var onFailure = function(transport, object) {
+
+ // We failed, but we at least know we're done waiting
hide_wait_message_now();
+ // Cry like a baby
alert("Unable to connect to server.\n\nTry again in a few minutes.");
+ // Record the failed request (XXX for debugging?)
Jifty.failedRequest = transport;
+ // Re-enable the forms, no sense in locking them up
for ( var i = 0; i < disabled_elements.length; i++ ) {
disabled_elements[i].disabled = false;
}
};
- // Build variable structure
+ // Almost ready to submit! Add the region arguments
request.variables = {};
jQuery.each(current_args, function(k, v) {
request.variables['region-'+k] = v;
});
- // Build continuation structure
+ // Add in the continuation information
request.continuation = named_args['continuation'];
- // Push any state variables which we set into the forms
+ // Update the region state information or add it, if needed
for (var i = 0; i < document.forms.length; i++) {
var form = document.forms[i];
@@ -1375,17 +1517,21 @@
})
}
- // Go!
+ // Submit ajax request as JSON; expect XML in return
jQuery.ajax({
- url: document.URL,
- type: 'post',
- dataType: 'xml',
- data: JSON.stringify(request),
+ url: document.URL,
+ type: 'post',
+ dataType: 'xml',
+ data: JSON.stringify(request),
contentType: 'text/x-json',
- error: onFailure,
- complete: function(){hide_wait_message()},
- success: onSuccess
+ error: onFailure,
+
+ // Hide the wait message when we're done
+ complete: function(){hide_wait_message()},
+ success: onSuccess
});
+
+ // Disable regular browser form submission (we're Ajaxing instead)
return false;
}
More information about the Jifty-commit
mailing list