vendor/assets/javascripts/hobo-jquery/hjq.js in hobo_jquery-1.4.0.pre6 vs vendor/assets/javascripts/hobo-jquery/hjq.js in hobo_jquery-1.4.0.pre7
- old
+ new
@@ -6,11 +6,27 @@
//used for javascript testing.
var num_updates = 0;
var methods = {
- init : function() {
+ /* call only once per document. */
+ initOnce: function() {
+ if(typeof History === 'object') { // History.js installed
+ $(window).on("statechange", function() {
+ var state = History.getState();
+ if(state.data.length==3) {
+ var form = $(state.data[0]);
+ var roptions = form.hjq('buildRequestCallbacks', state.data[1], state.data[2]);
+ $.ajax(state.url, roptions);
+ }
+ })
+ }
+ return true;
+ },
+
+ /* call for every new fragment */
+ init: function() {
var top = this;
this.find("[data-rapid-page-data]").each(function() {
page_data = $(this).data('rapid-page-data');
});
this.find("[data-rapid]").each(function() {
@@ -150,17 +166,40 @@
var f=methods.functionByName.call(this, script);
if(f) return f;
return function() { return eval(script); };
},
+
+ /* returns a jQuery selector for an element. One option would
+ be to use something like
+ http://stackoverflow.com/questions/2206958/best-way-to-reference-an-element-with-jquery
+ However, if the DOM changes due to Ajax this isn't
+ necessarily stable. So instead we give the element a unique
+ ID if it doesn't already have one.
+ */
+
+ getPath: function() {
+ if(!this.attr("id")) {
+ this.attr("id", Math.random().toString().replace("0.", "id"))
+ }
+ return "#"+this.attr("id");
+ },
+
+
/* Build an options object suitable for sending to
* jQuery.ajax. (Note that the before & confirm callbacks
* are called from this function, and the spinner is shown)
*
* The returned object will include a 'data' value
* populated with a hash.
*
+ * This function has now been split into two parts to
+ * better support push_state. buildRequestData is the
+ * first part, which builds everything except the
+ * callbacks (but it does execute the before callbacks).
+ * buildRequestCallbacks builds the remaining callbacks.
+ *
* Options:
* type: POST, GET
* attrs: a hash containing the standard Hobo ajax attributes & callbacks
* extra_options: merged into the hash sent to jQuery.ajax
* extra_callbacks: the callbacks in attrs are generally specified by the DRYML; this allows the framework to add their own
@@ -169,14 +208,17 @@
* postamble: passed to Hobo's ajax_update_response
* content_type: passed to Hobo's ajax_update_response
*
*/
buildRequest: function(o) {
+ return methods.buildRequestCallbacks.call(this, methods.buildRequestData.call(this, o), o);
+ },
+
+ buildRequestData: function(o) {
var that = this;
if (!o.attrs) o.attrs = {};
- if (!o.extra_callbacks) o.extra_callbacks = {};
- var options = {};
+ var result = {};
if(o.attrs.before) {
if(!methods.createFunction.call(that, o.attrs.before).call(this)) {
return false;
}
@@ -192,19 +234,19 @@
if(!confirm(o.attrs.confirm)) {
return false;
}
}
- options.context = this;
- options.type = o.type || 'GET';
- options.data = {"render_options[preamble]": o.preamble || '',
+ result.context = this;
+ result.type = o.type || 'GET';
+ result.data = {"render_options[preamble]": o.preamble || '',
"render_options[contexts_function]": 'hjq.ajax.updatePartContexts'
};
- if(o.postamble) options.data["render_options[postamble]"] = o.postamble;
- if(o.content_type) options.data["render_options[content_type]"] = o.content_type;
- if(o.attrs.errors_ok) options.data["render_options[errors_ok]"] = 1;
- options.dataType = 'script';
+ if(o.postamble) result.data["render_options[postamble]"] = o.postamble;
+ if(o.content_type) result.data["render_options[content_type]"] = o.content_type;
+ if(o.attrs.errors_ok) result.data["render_options[errors_ok]"] = 1;
+ result.dataType = 'script';
o.spec = jQuery.extend({'function': 'hjq.ajax.update', preamble: ''}, o.spec);
var part_data = {};
if(o.attrs.hide!==undefined) part_data.hide = o.attrs.hide;
if(o.attrs.show!==undefined) part_data.show = o.attrs.show;
@@ -212,15 +254,23 @@
// we tell our controller which parts to return by sending it a "render" array.
var ids=methods.getUpdateIds.call(this, o.attrs);
for(var i=0; i<ids.length; i++) {
if(part_data) $("#"+ids[i]).data('hjq-ajax', part_data);
- options.data["render["+i+"][part_context]"] = page_data.hobo_parts[ids[i]];
- options.data["render["+i+"][id]"] = ids[i];
- options.data["render["+i+"][function]"] = o['function'] || 'hjq.ajax.update';
+ result.data["render["+i+"][part_context]"] = page_data.hobo_parts[ids[i]];
+ result.data["render["+i+"][id]"] = ids[i];
+ result.data["render["+i+"][function]"] = o['function'] || 'hjq.ajax.update';
}
+ return result;
+ },
+
+ buildRequestCallbacks: function(result, o) {
+ var that = this;
+ if (!o.attrs) o.attrs = {};
+ if (!o.extra_callbacks) o.extra_callbacks = {};
+
this.hjq_spinner(o.attrs, "Saving...");
var success_dfd = jQuery.Deferred();
if(o.attrs.success) success_dfd.done(methods.createFunction.call(that, o.attrs.success));
if(o.extra_callbacks.success) success_dfd.done(methods.createFunction.call(that, o.extra_callbacks.success));
@@ -230,37 +280,55 @@
// have already been removed and we don't bubble
// up, so triggering on that won't do any good
if(that.parents("body").length==0) $(document).trigger('rapid:ajax:success', [that]);
else that.trigger('rapid:ajax:success', [that]);
});
- options.success = success_dfd.resolve;
+ result.success = success_dfd.resolve;
var error_dfd = jQuery.Deferred();
if(o.attrs.error) error_dfd.done(methods.createFunction.call(that, o.attrs.error));
if(o.extra_callbacks.error) error_dfd.done(methods.createFunction.call(that, o.extra_callbacks.error));
error_dfd.done(function() {
if(that.parents("body").length==0) $(document).trigger('rapid:ajax:error', [that]);
else that.trigger('rapid:ajax:error', [that]);
});
- options.error = error_dfd.resolve;
+ result.error = error_dfd.resolve;
var complete_dfd = jQuery.Deferred();
if(o.attrs.complete) complete_dfd.done(methods.createFunction.call(that, o.attrs.complete));
if(o.extra_callbacks.complete) complete_dfd.done(methods.createFunction.call(that, o.extra_callbacks.complete));
complete_dfd.done(function() {
if(that.parents("body").length==0) $(document).trigger('rapid:ajax:complete', [that]);
else that.trigger('rapid:ajax:complete', [that]);
that.hjq_spinner('remove');
if(o.attrs.refocus_form) that.find(":input[type!=hidden]:first").focus();
});
- options.complete = complete_dfd.resolve;
+ result.complete = complete_dfd.resolve;
- jQuery.extend(options, o.extra_options);
+ jQuery.extend(result, o.extra_options);
- return options;
+ return result;
},
+ /*
+ this: element to receive callbacks
+ url: new location
+ ajax_options: output from buildRequestData
+ hobo_options: input to buildRequestData, buildRequestCallbacks
+ */
+ changeLocationAjax: function (url, ajax_options, hobo_options) {
+ if (hobo_options.attrs.push_state && typeof History==='object') {
+ // if the history plugin is installed, it will fire the
+ // changestate event immediately, which is where we
+ // actually execute the ajax
+ window.History.pushState([this.getPath(), ajax_options, hobo_options], hobo_options.attrs.new_title || null, url);
+ } else {
+ ajax_options = this.hjq('buildRequestCallbacks', ajax_options, hobo_options);
+ $.ajax(url, ajax_options);
+ }
+ },
+
// given ajax_attrs (update, updates and/or ajax), return DOM id's.
getUpdateIds: function(attrs) {
var ids = attrs.update || [];
if (typeof ids=='string') ids=ids.split(',');
@@ -291,10 +359,10 @@
$.fn.hjq = function( method ) {
if ( methods[method] ) {
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
- return methods.init.apply( this, arguments );
+ return methods.initOnce.apply( this, arguments ) && methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist on hjq' );
}
};