aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--polldance.js242
1 files changed, 135 insertions, 107 deletions
diff --git a/polldance.js b/polldance.js
index 9fedaa6..b933e04 100644
--- a/polldance.js
+++ b/polldance.js
@@ -3,29 +3,62 @@
* Copyright 2013 Fan Out Networks, Inc.
* Released under the MIT license (see COPYING file in source distribution)
*/
+(function () {
+var DEBUG = true;
+"use strict";
(function (window, undefined) {
+ var NAMESPACE = "PollDance";
var TIMEOUT = 60000;
+ var emptyMethod = function(){};
- // don't break if there's no console
- if (typeof (window.console) === "undefined") {
- window.console = { log: function () { }, dir: function () { } };
+ var log;
+ if (DEBUG) {
+ // don't break if there's no console
+ if (typeof (window.console) === "undefined") {
+ window.console = { log: emptyMethod, dir: emptyMethod };
+ }
+ log = function(output) { window.console.log(output); };
+ } else {
+ log = emptyMethod;
}
- // PollDance.Request has on('finished', int code, object result, object headers) and on('error', int reason) callback members
+ var copyArray = function(array) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ return Array.prototype.slice.apply(array, args);
+ };
+
+ var indexOfItemInArray = function(array, item) {
+ for (var i = 0, length = array.length; i < length; i++) {
+ if (array[i] === item) {
+ return i;
+ }
+ }
+ return -1;
+ };
+ var removeFromArray = function (array, item) {
+ var again = true;
+ while (again) {
+ var index = indexOfItemInArray(array, item);
+ if (index != -1) {
+ array.splice(index, 1);
+ } else {
+ again = false;
+ }
+ }
+ };
- var JsonCallbacks = {
+ var jsonCallbacks = {
id: 0,
requests: {},
- emptyCallback: function() {},
getJsonpCallback: function (id) {
var cb;
var requests = this.requests;
if (id in this.requests) {
cb = function (result) { requests[id]._jsonp_callback(result); };
} else {
- console.log("no callback with id " + id);
- cb = this.emptyCallback;
+ log("no callback with id " + id);
+ cb = emptyMethod;
}
return cb;
},
@@ -45,99 +78,100 @@
}
};
- var corsAvailable = "withCredentials" in new XMLHttpRequest();
+ var corsAvailable = "withCredentials" in new window.XMLHttpRequest();
- function sameOrigin(url) {
+ var sameOrigin = function(url) {
var loc = window.location;
- var a = document.createElement('a');
+ var a = window.document.createElement('a');
a.href = url;
return !a.hostname || (a.hostname == loc.hostname && a.port == loc.port && a.protocol == loc.protocol);
- }
+ };
var Events = function () {
this._events = {};
};
- Events.prototype._getEventsForType = function (type) {
+ Events.prototype._getHandlersForType = function (type) {
if (!(type in this._events)) {
this._events[type] = [];
}
return this._events[type];
};
- Events.prototype._setEventsForType = function (type, events) {
- this._events[type] = events;
- };
Events.prototype.on = function (type, handler) {
- var events = this._getEventsForType(type);
- events.push(handler);
+ var handlers = this._getHandlersForType(type);
+ handlers.push(handler);
};
Events.prototype.off = function (type) {
if (arguments.length > 1) {
- var events = this._getEventsForType(type);
- var handlersToKeep = [];
- var handlers = Array.prototype.slice.call(arguments, 1);
- for (var i = 0; i < events.length; i++) {
- var event = events[i];
- var keep = true;
- for (var j = 0; j < handlers.length; j++) {
- var handler = handlers[j];
- if (event == handler) {
- keep = false;
- }
- }
- if (keep) {
- handlersToKeep.push(event);
- }
- }
- this._setEventsForType(type, handlersToKeep);
+ var handler = arguments[1];
+ var handlers = this._getHandlersForType(type);
+ removeFromArray(handlers, handler);
} else {
delete this._events[type];
}
-
};
Events.prototype.trigger = function (type, obj) {
- var args = Array.prototype.slice.call(arguments, 2);
- var events = this._getEventsForType(type);
- for (var i = 0; i < events.length; i++) {
- var handler = events[i];
+ var args = copyArray(arguments, 2);
+ var handlers = copyArray(this._getHandlersForType(type));
+ for (var i = 0, n = handlers.length; i < n; i++) {
+ var handler = handlers[i];
handler.apply(obj, args);
}
};
- var ErrorTypes = {
+ var errorTypes = {
"TransportError": 0,
"TimeoutError": 1
};
- var TransportTypes = {
+ var transportTypes = {
"Auto": 0,
"Xhr": 1,
"Jsonp": 2
};
- var Request = function (config) {
+ // PollDance.Request has callback members:
+ // on('finished', int code, object result, object headers)
+ // on('error', int reason)
+
+ var Request = function () {
if (!(this instanceof Request)) {
throw new Error("Constructor called as a function");
}
this._events = new Events();
+ this._tries = 0;
this._delayNext = false;
+ this._retryTime = 0;
+ this._timer = null;
+ this._callbackInfo = null;
+
+ this._xhr = null;
+ this._method = null;
+ this._url = null;
+ this._headers = null;
+ this._body = null;
+ this.transport = transportTypes.Auto;
this.rawResponse = false;
this.maxTries = 1;
this.maxDelay = 1000;
- if (config !== undefined) {
- if (config.transport !== undefined)
+ if (arguments.length > 0) {
+ var config = arguments[0];
+ if ("transport" in config) {
this.transport = config.transport;
- if (config.rawResponse !== undefined)
+ }
+ if ("rawResponse" in config) {
this.rawResponse = config.rawResponse;
- if (config.maxTries !== undefined)
+ }
+ if ("maxTries" in config) {
this.maxTries = config.maxTries;
- if (config.maxDelay !== undefined)
+ }
+ if ("maxDelay" in config) {
this.maxDelay = config.maxDelay;
+ }
}
};
- Request.prototype.transport = TransportTypes.Auto;
Request.prototype.start = function (method, url, headers, body) {
var self = this;
@@ -147,7 +181,7 @@
if (self._delayNext) {
self._delayNext = false;
delaytime = Math.floor(Math.random() * self.maxDelay);
- console.log("PD: polling again in " + delaytime + "ms");
+ log("PD: polling again in " + delaytime + "ms");
} else {
delaytime = 0; // always queue the call, to prevent browser "busy"
}
@@ -157,7 +191,7 @@
self._headers = headers;
self._body = body;
- self._timer = setTimeout(function() { self._connect(); }, delaytime);
+ self._timer = window.setTimeout(function () { self._connect(); }, delaytime);
};
Request.prototype.retry = function () {
this._retry();
@@ -166,19 +200,19 @@
var self = this;
self._timer = window.setTimeout(function () { self._timeout(); }, TIMEOUT);
- ++self._tries;
+ self._tries++;
- if (self.transport == TransportTypes.Auto) {
+ if (self.transport == transportTypes.Auto) {
if (corsAvailable || sameOrigin(self._url)) {
- self.transport = TransportTypes.Xhr;
+ self.transport = transportTypes.Xhr;
} else {
- self.transport = TransportTypes.Jsonp;
+ self.transport = transportTypes.Jsonp;
}
}
- if (self.transport == TransportTypes.Xhr) {
+ if (self.transport == transportTypes.Xhr) {
- self._xhr = new XMLHttpRequest();
+ self._xhr = new window.XMLHttpRequest();
self._xhr.onreadystatechange = function () { self._xhr_readystatechange(); };
self._xhr.open(self._method, self._url, true);
@@ -190,15 +224,15 @@
self._xhr.send(self._body);
- console.log("PD: xhr " + self._url + " " + self._body);
+ log("PD: xhr " + self._url + " " + self._body);
} else { // Jsonp
- this._callbackInfo = JsonCallbacks.newCallbackInfo();
+ this._callbackInfo = jsonCallbacks.newCallbackInfo();
var paramList = [];
- paramList.push("_callback=" + encodeURIComponent("PollDance._getJsonpCallback(\"" + this._callbackInfo.id + "\")"));
+ paramList.push("_callback=" + encodeURIComponent("window['" + NAMESPACE + "']._getJsonpCallback(\"" + this._callbackInfo.id + "\")"));
if (self._method != "GET") {
paramList.push("_method=" + encodeURIComponent(self._method));
}
@@ -217,12 +251,12 @@
src = self._url + "?" + params;
}
- console.log("PD: json-p " + this._callbackInfo.id + " " + src);
+ log("PD: json-p " + this._callbackInfo.id + " " + src);
this._addJsonpCallback(this._callbackInfo, src);
}
};
Request.prototype.abort = function () {
- clearTimeout(this._timer);
+ window.clearTimeout(this._timer);
this._timer = null;
this._cancelreq();
};
@@ -230,32 +264,33 @@
this._events.on(type, handler);
};
Request.prototype.off = function (type) {
- var args = Array.prototype.slice.call(arguments);
+ var args = copyArray(arguments, 1).unshift(type);
this._events.off.apply(this._events, args);
};
Request.prototype._addJsonpCallback = function (callbackInfo, src) {
- JsonCallbacks.addJsonpCallback(callbackInfo.id, this);
+ jsonCallbacks.addJsonpCallback(callbackInfo.id, this);
- var head = document.getElementsByTagName("head")[0];
- var script = document.createElement("script");
+ var script = window.document.createElement("script");
script.type = "text/javascript";
script.id = callbackInfo.scriptId;
script.src = src;
+
+ var head = window.document.getElementsByTagName("head")[0];
head.appendChild(script);
};
Request.prototype._removeJsonpCallback = function (callbackInfo) {
- JsonCallbacks.removeJsonpCallback(callbackInfo.id, this);
+ jsonCallbacks.removeJsonpCallback(callbackInfo.id, this);
- var script = document.getElementById(callbackInfo.scriptId);
+ var script = window.document.getElementById(callbackInfo.scriptId);
script.parentNode.removeChild(script);
};
Request.prototype._cancelreq = function () {
- if (this.transport == TransportTypes.Xhr) {
- this._xhr.onreadystatechange = function () { }
+ if (this.transport == transportTypes.Xhr) {
+ this._xhr.onreadystatechange = emptyMethod;
this._xhr.abort();
this._xhr = null;
} else { // Jsonp
- console.log("PD: json-p " + this._callbackInfo.id + " cancel");
+ log("PD: json-p " + this._callbackInfo.id + " cancel");
this._removeJsonpCallback(this._callbackInfo);
this._callbackInfo = null;
}
@@ -268,24 +303,21 @@
window.clearTimeout(this._timer);
this._timer = null;
- if (xhr.status) {
- if (xhr.status >= 500 && xhr.status <= 599 && (this.maxTries == -1 || this._tries < this.maxTries)) {
- this._retry();
- } else {
+ if ((!xhr.status || (xhr.status >= 500 && xhr.status < 600)) &&
+ (this.maxTries == -1 || this._tries < this.maxTries)) {
+ this._retry();
+ } else {
+ if (xhr.status) {
// TODO: xhr.getAllResponseHeaders()
this._handle_response(xhr.status, xhr.statusText, {}, xhr.responseText);
- }
- } else {
- if (this.maxTries == -1 || this._tries < this.maxTries) {
- this._retry();
} else {
- this._error(ErrorTypes.TransportError);
+ this._error(errorTypes.TransportError);
}
}
}
};
Request.prototype._jsonp_callback = function (result) {
- console.log("PD: json-p " + this._callbackInfo.id + " finished");
+ log("PD: json-p " + this._callbackInfo.id + " finished");
window.clearTimeout(this._timer);
this._timer = null;
@@ -293,27 +325,21 @@
this._removeJsonpCallback(this._callbackInfo);
this._callbackInfo = null;
- var headers;
- if (result.headers !== undefined) {
- headers = result.headers;
- } else {
- headers = {};
- }
-
+ var headers = ("headers" in result) ? result.headers : {};
this._handle_response(result.code, result.status, headers, result.body);
};
Request.prototype._handle_response = function (code, status, headers, body) {
- var result;
- if (this.rawResponse) {
- result = body;
- } else {
- try {
- result = JSON.parse(body);
- } catch (e) {
- result = body;
- }
- }
- this._finished(code, result, headers);
+ var result;
+ if (this.rawResponse) {
+ result = body;
+ } else {
+ try {
+ result = JSON.parse(body);
+ } catch (e) {
+ result = body;
+ }
+ }
+ this._finished(code, result, headers);
};
Request.prototype._timeout = function () {
this._timer = null;
@@ -322,10 +348,10 @@
if (this.maxTries == -1 || this._tries < this.maxTries) {
this._retry();
} else {
- this._error(ErrorTypes.TimeoutError);
+ this._error(errorTypes.TimeoutError);
}
};
- Request.prototype._retry = function() {
+ Request.prototype._retry = function () {
if (this._tries === 1) {
this._retryTime = 1;
} else if (this._tries < 8) {
@@ -334,10 +360,10 @@
var delaytime = this._retryTime * 1000;
delaytime += Math.floor(Math.random() * this.maxDelay);
- console.log("PD: trying again in " + delaytime + "ms");
+ log("PD: trying again in " + delaytime + "ms");
var self = this;
- self._timer = setTimeout(function() { self._connect(); }, delaytime);
+ self._timer = window.setTimeout(function () { self._connect(); }, delaytime);
};
Request.prototype._finished = function (code, result, headers) {
this._delayNext = true;
@@ -348,13 +374,15 @@
this._events.trigger('error', this, reason);
};
- var pollDance = {
+ var exports = {
Request: Request,
+ TransportTypes: transportTypes,
+ ErrorTypes: errorTypes,
_getJsonpCallback: function (id) {
- return JsonCallbacks.getJsonpCallback(id);
+ return jsonCallbacks.getJsonpCallback(id);
}
};
-
- window.PollDance = window.PollDance || pollDance;
+ window[NAMESPACE] = window[NAMESPACE] || exports;
})(window);
+})();