diff options
-rw-r--r-- | polldance.js | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/polldance.js b/polldance.js new file mode 100644 index 0000000..906aed6 --- /dev/null +++ b/polldance.js @@ -0,0 +1,192 @@ +// don't break if there's no console +if(typeof(console) === "undefined") +{ + console = { log: function() {} }; +} + +var FO = FO || {}; + +var fo_jsonp_callback_id = 0; +var fo_jsonp_callbacks = {}; + +function fo_jsonp_get_callback(cb_id) +{ + if(fo_jsonp_callbacks[cb_id]) + { + return fo_jsonp_callbacks[cb_id]; + } + else + { + console.log("no callback with id " + cb_id); + return function(result) {}; + } +} + +// FO.Request has onFinished(int code, object result) and onError(int reason) callback members +// error reasons: 0=http, 1=timeout +// note: failed json parse on http body results in empty object {}, not error + +FO.Request = function() +{ + if(!(this instanceof FO.Request)) + throw new Error("Constructor called as a function"); +} + +FO.Request.prototype.start = function(method, url, headers, body) +{ + this._cors = false; + if("withCredentials" in new XMLHttpRequest()) + this._cors = true; + + var self = this; + this._timer = setTimeout(function() { self._timeout(); }, 60000); + + if(this._cors) + { + this._xhr = new XMLHttpRequest(); + + this._xhr.onreadystatechange = function() { self._xhr_readystatechange(); }; + this._xhr.open(method, url, true); + + for(var key in headers) + { + if(!headers.hasOwnProperty(key)) + continue; + + this._xhr.setRequestHeader(key, headers[key]); + } + + this._xhr.send(body); + } + else + { + var head = document.getElementsByTagName("head")[0]; + var script = document.createElement("script"); + + script.type = "text/javascript"; + + this._cb_id = fo_jsonp_callback_id.toString(); + ++fo_jsonp_callback_id; + + this._script_id = "fo-jsonp-script-" + this._cb_id; + script.id = this._script_id; + + fo_jsonp_callbacks[this._cb_id] = function(result) { self._jsonp_callback(result); }; + + var param_list = new Array(); + + param_list.push("callback=" + encodeURIComponent("fo_jsonp_get_callback(\"" + this._cb_id + "\")")); + if(method != "GET") + param_list.push("method=" + encodeURIComponent(method)); + if(headers) + param_list.push("headers=" + encodeURIComponent(JSON.stringify(headers))); + if(body) + param_list.push("body=" + encodeURIComponent(body)); + + var params = param_list.join("&"); + + var src; + if(url.indexOf("?") != -1) + src = url + "&" + params; + else + src = url + "?" + params; + + script.src = src; + console.log("FO.Request json-p " + this._cb_id + " " + src); + head.appendChild(script); + } +} + +FO.Request.prototype.abort = function() +{ + clearTimeout(this._timer); + this._timer = null; + + this._cancelreq(); +} + +FO.Request.prototype._cancelreq = function() +{ + if(this._cors) + { + this._xhr.onreadystatechange = function() {} + this._xhr.abort(); + this._xhr = null; + } + else + { + console.log("FO.Request json-p " + this._cb_id + " timeout"); + + delete fo_jsonp_callbacks[this._cb_id]; + var script = document.getElementById(this._script_id); + script.parentNode.removeChild(script); + this._cb_id = null; + this._script_id = null; + } +} + +FO.Request.prototype._xhr_readystatechange = function() +{ + if(this._xhr.readyState === 4) + { + clearTimeout(this._timer); + this._timer = null; + + var status = this._xhr.status; + var responseText = this._xhr.responseText; + this._xhr = null; + + if(status) + { + var result; + + try + { + result = JSON.parse(responseText); + } + catch(e) + { + result = {} + } + + this._finished(status, result); + } + else + this._error(0); + } +} + +FO.Request.prototype._jsonp_callback = function(result) +{ + console.log("FO.Request json-p " + this._cb_id + " finished"); + + clearTimeout(this._timer); + this._timer = null; + + delete fo_jsonp_callbacks[this._cb_id]; + var script = document.getElementById(this._script_id); + script.parentNode.removeChild(script); + this._cb_id = null; + this._script_id = null; + + this._finished(result.status, result.value); +} + +FO.Request.prototype._timeout = function() +{ + this._timer = null; + this._cancelreq(); + this._error(1); +} + +FO.Request.prototype._finished = function(code, result) +{ + if(this.onFinished) + this.onFinished(code, result); +} + +FO.Request.prototype._error = function(reason) +{ + if(this.onError) + this.onError(reason); +} |