/** * PeriodicalUpdater - jQuery plugin for timed, decaying ajax calls * * http://www.360innovate.co.uk/blog/2009/03/periodicalupdater-for-jquery/ * http://enfranchisedmind.com/blog/posts/jquery-periodicalupdater-ajax-polling/ * * Copyright (c) 2009 by the following: * Frank White (http://customcode.info) * Robert Fischer (http://smokejumperit.com) * 360innovate (http://www.360innovate.co.uk) * * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html * */ (function($) { var pu_log = function(msg) { try { console.log(msg); } catch(err) {} } // Now back to our regularly scheduled work $.PeriodicalUpdater = function(url, options, callback, autoStopCallback){ var settings = jQuery.extend(true, { url: url, // URL of ajax request cache: false, // By default, don't allow caching method: 'GET', // method; get or post data: '', // array of values to be passed to the page - e.g. {name: "John", greeting: "hello"} minTimeout: 1000, // starting value for the timeout in milliseconds maxTimeout: 8000, // maximum length of time between requests multiplier: 2, // if set to 2, timerInterval will double each time the response hasn't changed (up to maxTimeout) maxCalls: 0, // maximum number of calls. 0 = no limit. autoStop: 0 // automatically stop requests after this many returns of the same data. 0 = disabled }, options); // set some initial values, then begin var timer = null; var timerInterval = settings.minTimeout; var maxCalls = settings.maxCalls; var autoStop = settings.autoStop; var calls = 0; var noChange = 0; var originalMaxCalls = maxCalls; var reset_timer = function(interval) { if (timer != null) { clearTimeout(timer); } timerInterval = interval; pu_log('resetting timer to '+ timerInterval +'.'); timer = setTimeout(getdata, timerInterval); } // Function to boost the timer var boostPeriod = function() { if(settings.multiplier >= 1) { before = timerInterval; timerInterval = timerInterval * settings.multiplier; if(timerInterval > settings.maxTimeout) { timerInterval = settings.maxTimeout; } after = timerInterval; pu_log('adjusting timer from '+ before +' to '+ after +'.'); reset_timer(timerInterval); } }; // Construct the settings for $.ajax based on settings var ajaxSettings = jQuery.extend(true, {}, settings); if(settings.type && !ajaxSettings.dataType) ajaxSettings.dataType = settings.type; if(settings.sendData) ajaxSettings.data = settings.sendData; ajaxSettings.type = settings.method; // 'type' is used internally for jQuery. Who knew? ajaxSettings.ifModified = true; var handle = { restart: function() { maxCalls = originalMaxCalls; calls = 0; reset_timer(timerInterval); return; }, stop: function() { maxCalls = -1; return; } }; // Create the function to get data // TODO It'd be nice to do the options.data check once (a la boostPeriod) function getdata() { var toSend = jQuery.extend(true, {}, ajaxSettings); // jQuery screws with what you pass in if(typeof(options.data) == 'function') { toSend.data = options.data(); if(toSend.data) { // Handle transformations (only strings and objects are understood) if(typeof(toSend.data) == "number") { toSend.data = toSend.data.toString(); } } } if(maxCalls == 0) { $.ajax(toSend); } else if(maxCalls > 0 && calls < maxCalls) { $.ajax(toSend); calls++; } } // Implement the tricky behind logic var remoteData = null; var prevData = null; ajaxSettings.success = function(data) { pu_log("Successful run! (In 'success')"); remoteData = data; // timerInterval = settings.minTimeout; }; ajaxSettings.complete = function(xhr, success) { //pu_log("Status of call: " + success + " (In 'complete')"); if(maxCalls == -1) return; if(success == "success" || success == "notmodified") { var rawData = $.trim(xhr.responseText); if(rawData == 'STOP_AJAX_CALLS') { handle.stop(); return; } if(prevData == rawData) { if(autoStop > 0) { noChange++; if(noChange == autoStop) { handle.stop(); if(autoStopCallback) autoStopCallback(noChange); return; } } boostPeriod(); } else { noChange = 0; reset_timer(settings.minTimeout); prevData = rawData; if(remoteData == null) remoteData = rawData; // jQuery 1.4+ $.ajax() automatically converts "data" into a JS Object for "type:json" requests now // For compatibility with 1.4+ and pre1.4 jQuery only try to parse actual strings, skip when remoteData is already an Object if((ajaxSettings.dataType === 'json') && (typeof(remoteData) === 'string') && (success == "success")) { remoteData = JSON.parse(remoteData); } if(settings.success) { settings.success(remoteData, success, xhr, handle); } if(callback) callback(remoteData, success, xhr, handle); } } remoteData = null; } ajaxSettings.error = function (xhr, textStatus) { //pu_log("Error message: " + textStatus + " (In 'error')"); if(textStatus != "notmodified") { prevData = null; reset_timer(settings.minTimeout); } if(settings.error) { settings.error(xhr, textStatus); } }; // Make the first call $(function() { reset_timer(timerInterval); }); return handle; }; })(jQuery);