From b21e676a71709eb9ef9364bce3a94e6be07c4f26 Mon Sep 17 00:00:00 2001 From: David Campey Date: Wed, 10 Oct 2012 11:14:40 +0200 Subject: [PATCH 1/2] fix for not detecting changes on json requests This fix copies stringify(data) into xhr.responseText if xhr.responseText is undefined. When making a cross-domain JSON query, the xhr.responseText comes back as undefined. This meant that comparing this across calls wasn't working to identify changes and smart updater wouldn't update on subsequent calls. Now it Works. --- smartupdater.js | 598 ++++++++++++++++++++++++------------------------ 1 file changed, 302 insertions(+), 296 deletions(-) diff --git a/smartupdater.js b/smartupdater.js index 8609636..11efe4f 100644 --- a/smartupdater.js +++ b/smartupdater.js @@ -1,296 +1,302 @@ -/** -* smartupdater - jQuery Plugin -* -* Version - 4.0 -* Copyright (c) 2010 - 2012 Vadim Kiryukhin -* vkiryukhin @ gmail.com -* -* http://www.eslinstructor.net/smartupdater/ -* -* Dual licensed under the MIT and GPL licenses: -* http://www.opensource.org/licenses/mit-license.php -* http://www.gnu.org/licenses/gpl.html -* -* USAGE: -* -* $("#myObject").smartupdater({ -* url : "foo.php" -* }, function (data) { -* //process data here; -* } -* ); -* -* Public functions: -* $("#myObject").smartupdater("stop") -* $("#myObject").smartupdater("restart"); -* $("#myObject").smartupdater("setTimeout",timeout); -* $("#myObject").smartupdater("alterUrl"[,"foo.php"[,data]]); -* $("#myObject").smartupdater("alterCallback"[, foo]); -* -* Public Attributes: -* var status = $("#myObject").smartupdater("getState"); -* var timeout = $("#myObject").smartupdater("getTimeout"); -* -**/ - -(function($) { - - - var methods = { - - init : function( options, callback) { - return this.each(function () { - var elem = this, - es = {}; - - elem.settings = jQuery.extend(true,{ - url : '', // see jQuery.ajax for details - type : 'get', // see jQuery.ajax for details - data : '', // see jQuery.ajax for details - dataType : 'text', // see jQuery.ajax for details - - minTimeout : 60000, // 1 minute - maxFailedRequests : 10, // max. number of consecutive ajax failures - maxFailedRequestsCb : false, // falure callback function - httpCache : false, // http cache - rCallback : false, // remote callback functions - selfStart : true, // start automatically after initializing - smartStop : { active: false, //disabled by default - monitorTimeout: 2500, // 2.5 seconds - minHeight: 1, // 1px - minWidth: 1 // 1px - } - - }, options); - - elem.smartupdaterStatus = {state:'',timeout:0}; - - es = elem.settings; - - es.prevContent = ''; - es.failedRequests = 0; - es.etag = '0'; - es.lastModified = '0'; - es.callback = callback; - es.origReq = {url:es.url,data:es.data,callback:callback}; - es.stopFlag = false; - - - function start() { - - /* check if element has been deleted and clean it up */ - if(!$(elem).parents('body').length) { - clearInterval(elem.smartupdaterStatus.smartStop); - clearTimeout(elem.settings.h); - elem = {}; - return; - } - - $.ajax({ - url : es.url, - type : es.type, - data : es.data, - dataType: es.dataType, - cache : false, // MUST be set to false to prevent IE caching issue. - - success: function (data, statusText, xhr) { - - var dataNotModified = false, - rCallback = false, - xSmart = jQuery.parseJSON(xhr.getResponseHeader("X-Smartupdater")), - xhrEtag, xhrLM; - - if(xSmart) { // remote control - - /* remote timeout */ - es.minTimeout = xSmart.timeout ? xSmart.timeout : es.minTimeout; - - /* remote callback */ - rCallback = xSmart.callback ? xSmart.callback : false; - } - - if(es.httpCache) { // http cache process here - - xhrEtag = xhr.getResponseHeader("ETag"); - xhrLM = xhr.getResponseHeader("Last-Modified"); - - dataNotModified = (es.etag == xhrEtag || es.lastModified == xhrLM) ? true : false; - es.etag = xhrEtag ? xhrEtag : es.etag; - es.lastModified = xhrLM ? xhrLM : es.lastModified; - } - - if ( dataNotModified || - es.prevContent == xhr.responseText || - xhr.status == 304 ) { // data is not changed - - if(!es.stopFlag) { - clearTimeout(es.h); - es.h = setTimeout(start, es.minTimeout); - } - - } else { // data is changed - - /* cache response data */ - es.prevContent = xhr.responseText; - - /* reset timeout */ - if(!es.stopFlag) { - clearTimeout(es.h); - es.h = setTimeout(start, es.minTimeout); - } - - /* run callback function */ - if(es.rCallback && rCallback && es.rCallback.search(rCallback) != -1) { - window[rCallback](data); - } else { - es.callback(data); - } - } - - elem.smartupdaterStatus.timeout = es.minTimeout; - es.failedRequests = 0; - }, - - error: function(xhr, textStatus, errorThrown) { - if ( ++es.failedRequests < es.maxFailedRequests ) { - - /* increment falure counter and reset timeout */ - if(!es.stopFlag) { - clearTimeout(es.h); - es.h = setTimeout(start, es.minTimeout); - elem.smartupdaterStatus.timeout = es.minTimeout; - } - - } else { - - /* stop smartupdater */ - clearTimeout(es.h); - elem.smartupdaterStatus.state = 'OFF'; - if( typeof(es.maxFailedRequestsCb)==='function') { - es.maxFailedRequestsCb(xhr, textStatus, errorThrown); - } - } - }, - - beforeSend: function(xhr, settings) { - - if(es.httpCache) { - - /* set http cache-related headers */ - xhr.setRequestHeader("If-None-Match", es.etag ); - xhr.setRequestHeader("If-Modified-Since", es.lastModified ); - } - - /* Feedback: Smartupdater sends it's current timeout to server */ - xhr.setRequestHeader("X-Smartupdater", '{"timeout":"'+elem.smartupdaterStatus.timeout+'"}'); - } - }); - - elem.smartupdaterStatus.state = 'ON'; - } - - es.fnStart = start; - - if(es.selfStart) { - start(); - } - - if(es.smartStop.active) { - - elem.smartupdaterStatus.smartStop = setInterval(function(){ - - // check if object has been deleted - if(!$(elem).parents('body').length) { - clearInterval(elem.smartupdaterStatus.smartStop); - clearTimeout(elem.settings.h); - elem = {}; - return; - } - - var $elem = $(elem); - var width = $elem.width(), - height = $elem.height(), - hidden = $elem.is(":hidden"); - - //element has been expanded, so smartupdater should be re-started. - if(!hidden && height > es.smartStop.minHeight && width > es.smartStop.minWidth - && elem.smartupdaterStatus.state=="OFF") { - $elem.smartupdater("restart"); - } else - //element has been minimized, so smartupdater should be stopped. - if( (hidden || height <= es.smartStop.minHeight || width <= es.smartStop.minWidth) - && elem.smartupdaterStatus.state=="ON") { - $elem.smartupdater("stop"); - - } - - },es.smartStop.monitorTimeout); - } - - }); - - },// init() - - stop : function () { - return this.each(function () { - this.settings.stopFlag = true; - clearTimeout(this.settings.h); - this.smartupdaterStatus.state = 'OFF'; - }); - }, - - restart : function () { - return this.each(function () { - this.settings.stopFlag = false; - clearTimeout(this.settings.h); - this.settings.failedRequests = 0; - this.settings.etag = "0"; - this.settings.lastModified = "0"; - this.settings.fnStart(); - }) - }, - - setTimeout : function (period) { - return this.each(function () { - clearTimeout(this.settings.h); - this.settings.minTimeout = period; - this.settings.fnStart(); - }); - }, - - alterCallback : function (callback) { - return this.each(function () { - this.settings.callback = callback ? callback : this.settings.origReq.callback; - }); - }, - - alterUrl : function (url,data) { - return this.each(function () { - this.settings.url = url ? url : this.settings.origReq.url; - this.settings.data = data ? data : this.settings.origReq.data; - }); - }, - - getTimeout : function () { - return this[0].smartupdaterStatus.timeout; - }, - - getState : function () { - return this[0].smartupdaterStatus.state; - } - - }; //methods - - jQuery.fn.smartupdater = function (options, callback) { - - if ( methods[options] ) { - return methods[ options ].apply( this, Array.prototype.slice.call( arguments, 1 )); - } else if ( typeof options === 'object' || ! method ) { - return methods.init.apply( this, arguments ); - } else { - $.error( 'Method ' + options + ' does not exist on jQuery.smartupdater' ); - } - }; - - -})(jQuery); +/** +* smartupdater - jQuery Plugin +* +* Version - 4.0 +* Copyright (c) 2010 - 2012 Vadim Kiryukhin +* vkiryukhin @ gmail.com +* +* http://www.eslinstructor.net/smartupdater/ +* +* Dual licensed under the MIT and GPL licenses: +* http://www.opensource.org/licenses/mit-license.php +* http://www.gnu.org/licenses/gpl.html +* +* USAGE: +* +* $("#myObject").smartupdater({ +* url : "foo.php" +* }, function (data) { +* //process data here; +* } +* ); +* +* Public functions: +* $("#myObject").smartupdater("stop") +* $("#myObject").smartupdater("restart"); +* $("#myObject").smartupdater("setTimeout",timeout); +* $("#myObject").smartupdater("alterUrl"[,"foo.php"[,data]]); +* $("#myObject").smartupdater("alterCallback"[, foo]); +* +* Public Attributes: +* var status = $("#myObject").smartupdater("getState"); +* var timeout = $("#myObject").smartupdater("getTimeout"); +* +**/ + +(function($) { + + + var methods = { + + init : function( options, callback) { + return this.each(function () { + var elem = this, + es = {}; + + elem.settings = jQuery.extend(true,{ + url : '', // see jQuery.ajax for details + type : 'get', // see jQuery.ajax for details + data : '', // see jQuery.ajax for details + dataType : 'text', // see jQuery.ajax for details + + minTimeout : 60000, // 1 minute + maxFailedRequests : 10, // max. number of consecutive ajax failures + maxFailedRequestsCb : false, // falure callback function + httpCache : false, // http cache + rCallback : false, // remote callback functions + selfStart : true, // start automatically after initializing + smartStop : { active: false, //disabled by default + monitorTimeout: 2500, // 2.5 seconds + minHeight: 1, // 1px + minWidth: 1 // 1px + } + + }, options); + + elem.smartupdaterStatus = {state:'',timeout:0}; + + es = elem.settings; + + es.prevContent = ''; + es.failedRequests = 0; + es.etag = '0'; + es.lastModified = '0'; + es.callback = callback; + es.origReq = {url:es.url,data:es.data,callback:callback}; + es.stopFlag = false; + + + function start() { + + /* check if element has been deleted and clean it up */ + if(!$(elem).parents('body').length) { + clearInterval(elem.smartupdaterStatus.smartStop); + clearTimeout(elem.settings.h); + elem = {}; + return; + } + + $.ajax({ + url : es.url, + type : es.type, + data : es.data, + dataType: es.dataType, + cache : false, // MUST be set to false to prevent IE caching issue. + + success: function (data, statusText, xhr) { + + var dataNotModified = false, + rCallback = false, + xSmart = jQuery.parseJSON(xhr.getResponseHeader("X-Smartupdater")), + xhrEtag, xhrLM; + + if(xSmart) { // remote control + + /* remote timeout */ + es.minTimeout = xSmart.timeout ? xSmart.timeout : es.minTimeout; + + /* remote callback */ + rCallback = xSmart.callback ? xSmart.callback : false; + } + + if(es.httpCache) { // http cache process here + + xhrEtag = xhr.getResponseHeader("ETag"); + xhrLM = xhr.getResponseHeader("Last-Modified"); + + dataNotModified = (es.etag == xhrEtag || es.lastModified == xhrLM) ? true : false; + es.etag = xhrEtag ? xhrEtag : es.etag; + es.lastModified = xhrLM ? xhrLM : es.lastModified; + } + + if(typeof xhr.responseText === 'undefined') { + // xhr.responseText isn't set in certain cases + // e.g. cross-domain JSON calls to twitter search + xhr.responseText = window.JSON.stringify(data); + } + + if ( dataNotModified || + es.prevContent == xhr.responseText || + xhr.status == 304 ) { // data is not changed + + if(!es.stopFlag) { + clearTimeout(es.h); + es.h = setTimeout(start, es.minTimeout); + } + + } else { // data is changed + + /* cache response data */ + es.prevContent = xhr.responseText; + + /* reset timeout */ + if(!es.stopFlag) { + clearTimeout(es.h); + es.h = setTimeout(start, es.minTimeout); + } + + /* run callback function */ + if(es.rCallback && rCallback && es.rCallback.search(rCallback) != -1) { + window[rCallback](data); + } else { + es.callback(data); + } + } + + elem.smartupdaterStatus.timeout = es.minTimeout; + es.failedRequests = 0; + }, + + error: function(xhr, textStatus, errorThrown) { + if ( ++es.failedRequests < es.maxFailedRequests ) { + + /* increment falure counter and reset timeout */ + if(!es.stopFlag) { + clearTimeout(es.h); + es.h = setTimeout(start, es.minTimeout); + elem.smartupdaterStatus.timeout = es.minTimeout; + } + + } else { + + /* stop smartupdater */ + clearTimeout(es.h); + elem.smartupdaterStatus.state = 'OFF'; + if( typeof(es.maxFailedRequestsCb)==='function') { + es.maxFailedRequestsCb(xhr, textStatus, errorThrown); + } + } + }, + + beforeSend: function(xhr, settings) { + + if(es.httpCache) { + + /* set http cache-related headers */ + xhr.setRequestHeader("If-None-Match", es.etag ); + xhr.setRequestHeader("If-Modified-Since", es.lastModified ); + } + + /* Feedback: Smartupdater sends it's current timeout to server */ + xhr.setRequestHeader("X-Smartupdater", '{"timeout":"'+elem.smartupdaterStatus.timeout+'"}'); + } + }); + + elem.smartupdaterStatus.state = 'ON'; + } + + es.fnStart = start; + + if(es.selfStart) { + start(); + } + + if(es.smartStop.active) { + + elem.smartupdaterStatus.smartStop = setInterval(function(){ + + // check if object has been deleted + if(!$(elem).parents('body').length) { + clearInterval(elem.smartupdaterStatus.smartStop); + clearTimeout(elem.settings.h); + elem = {}; + return; + } + + var $elem = $(elem); + var width = $elem.width(), + height = $elem.height(), + hidden = $elem.is(":hidden"); + + //element has been expanded, so smartupdater should be re-started. + if(!hidden && height > es.smartStop.minHeight && width > es.smartStop.minWidth + && elem.smartupdaterStatus.state=="OFF") { + $elem.smartupdater("restart"); + } else + //element has been minimized, so smartupdater should be stopped. + if( (hidden || height <= es.smartStop.minHeight || width <= es.smartStop.minWidth) + && elem.smartupdaterStatus.state=="ON") { + $elem.smartupdater("stop"); + + } + + },es.smartStop.monitorTimeout); + } + + }); + + },// init() + + stop : function () { + return this.each(function () { + this.settings.stopFlag = true; + clearTimeout(this.settings.h); + this.smartupdaterStatus.state = 'OFF'; + }); + }, + + restart : function () { + return this.each(function () { + this.settings.stopFlag = false; + clearTimeout(this.settings.h); + this.settings.failedRequests = 0; + this.settings.etag = "0"; + this.settings.lastModified = "0"; + this.settings.fnStart(); + }) + }, + + setTimeout : function (period) { + return this.each(function () { + clearTimeout(this.settings.h); + this.settings.minTimeout = period; + this.settings.fnStart(); + }); + }, + + alterCallback : function (callback) { + return this.each(function () { + this.settings.callback = callback ? callback : this.settings.origReq.callback; + }); + }, + + alterUrl : function (url,data) { + return this.each(function () { + this.settings.url = url ? url : this.settings.origReq.url; + this.settings.data = data ? data : this.settings.origReq.data; + }); + }, + + getTimeout : function () { + return this[0].smartupdaterStatus.timeout; + }, + + getState : function () { + return this[0].smartupdaterStatus.state; + } + + }; //methods + + jQuery.fn.smartupdater = function (options, callback) { + + if ( methods[options] ) { + return methods[ options ].apply( this, Array.prototype.slice.call( arguments, 1 )); + } else if ( typeof options === 'object' || ! method ) { + return methods.init.apply( this, arguments ); + } else { + $.error( 'Method ' + options + ' does not exist on jQuery.smartupdater' ); + } + }; + + +})(jQuery); From 78cbd22a86f4bb8413b1de72c5bbbd63dee67c07 Mon Sep 17 00:00:00 2001 From: Pieter Breed Date: Thu, 18 Oct 2012 14:53:34 +0200 Subject: [PATCH 2/2] fix for not detecting changes on json requests This fix copies stringify(data) into xhr.responseText if xhr.responseText is undefined. When making a cross-domain JSON query, the xhr.responseText comes back as undefined. This meant that comparing this across calls wasn't working to identify changes and smart updater wouldn't update on subsequent calls. Now it Works. (hopefully not screwing around with whitespace) --- smartupdater.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/smartupdater.js b/smartupdater.js index 8609636..e07cc16 100644 --- a/smartupdater.js +++ b/smartupdater.js @@ -118,6 +118,12 @@ es.etag = xhrEtag ? xhrEtag : es.etag; es.lastModified = xhrLM ? xhrLM : es.lastModified; } + ++ if(typeof xhr.responseText === 'undefined') { ++ // xhr.responseText isn't set in certain cases ++ // e.g. cross-domain JSON calls to twitter search ++ xhr.responseText = window.JSON.stringify(data); ++ } if ( dataNotModified || es.prevContent == xhr.responseText ||