From 74e587b1b6d6349f50c082ed7afece8d43be546d Mon Sep 17 00:00:00 2001 From: Justin Karneges Date: Fri, 12 Apr 2013 02:50:21 -0700 Subject: rename to pollymer --- Makefile | 14 +- README | 24 +-- polldance.js | 467 ----------------------------------------------------------- pollymer.js | 467 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 486 insertions(+), 486 deletions(-) delete mode 100644 polldance.js create mode 100644 pollymer.js diff --git a/Makefile b/Makefile index d970841..fc77805 100644 --- a/Makefile +++ b/Makefile @@ -3,15 +3,15 @@ VERSION = 1.0.0 all: dist distclean: - rm -f polldance-$(VERSION).js polldance-$(VERSION).min.js + rm -f pollymer-$(VERSION).js pollymer-$(VERSION).min.js clean: -dist: polldance-$(VERSION).min.js +dist: pollymer-$(VERSION).min.js -polldance-$(VERSION).js: polldance.js - cp polldance.js polldance-$(VERSION).js +pollymer-$(VERSION).js: pollymer.js + cp pollymer.js pollymer-$(VERSION).js -polldance-$(VERSION).min.js: polldance-$(VERSION).js - sed -e "s/DEBUG = true/DEBUG = false/g" polldance-$(VERSION).js | ./compile.py > polldance-$(VERSION).min.js.tmp - mv polldance-$(VERSION).min.js.tmp polldance-$(VERSION).min.js +pollymer-$(VERSION).min.js: pollymer-$(VERSION).js + sed -e "s/DEBUG = true/DEBUG = false/g" pollymer-$(VERSION).js | ./compile.py > pollymer-$(VERSION).min.js.tmp + mv pollymer-$(VERSION).min.js.tmp pollymer-$(VERSION).min.js diff --git a/README b/README index 11a8d0e..cbb49db 100644 --- a/README +++ b/README @@ -1,11 +1,11 @@ -PollDance ---------- -Date: January 31st, 2013 +Pollymer +-------- +Date: April 12th, 2013 Authors: Justin Karneges Katsuyuki Ohmuro Mailing List: http://lists.fanout.io/listinfo.cgi/fanout-users-fanout.io -PollDance is a general-purpose AJAX library that provides conveniences for +Pollymer is a general-purpose AJAX library that provides conveniences for long-polling applications, such as request retries, exponential backoff between requests, randomized request delaying, and workarounds for browser "busy" indications. It also implements multiple transports to ensure @@ -32,7 +32,7 @@ Limitations: Usage: - var req = new PollDance.Request(); + var req = new Pollymer.Request(); req.on('finished', function(code, result, headers) { ... }); req.on('error', function(reason) { ... }); var headers = { ... }; @@ -52,7 +52,7 @@ Methods of Request Object: result: JSON object or string headers: hash of key/value strings 'error': function(reason) - reason: PollDance.errorType + reason: Pollymer.errorType off(event_name) - Remove callback for event @@ -70,7 +70,7 @@ Methods of Request Object: (unless the recurring flag is set, see below). If called again on the same object, a short random delay will be added before performing the request. - retry() - Attempt the exact same request again. Normally, PollDance will + retry() - Attempt the exact same request again. Normally, Pollymer will automatically retry a request that it considers to be a failure, but this method may be used if the application needs to retry the request for any another reason. Retries have an exponentially increasing delay between @@ -85,7 +85,7 @@ Properties of Request Object: Properties are simple members of the object. They can be set directly: req.rawResponse = true; or passed in a hash during construction: - var req = new PollDance.Request({rawResponse: true}); + var req = new Pollymer.Request({rawResponse: true}); rawResponse: boolean By default, this library will parse response body data as JSON and return @@ -104,11 +104,11 @@ Properties of Request Object: recurring: boolean If set to true, then after a request finishes with a code between 200 and 299, and the finished event has been raised, the same request will be - started again. This allows PollDance to automatically poll a resource + started again. This allows Pollymer to automatically poll a resource endlessly. Pass a function as the url argument in order to be able to change the url between polls. - transport: PollDance.transportType + transport: Pollymer.transportType Explicitly set the transport to use. Default is transportType.Auto, which automatically chooses the best transport when the request is started. @@ -116,11 +116,11 @@ Retries: When a request fails at the transport level, or the request succeeds with an error code between 500 and 599, and maxTries has not been reached, then - PollDance will retry the request silently, with an exponentially increasing + Pollymer will retry the request silently, with an exponentially increasing delay between attempts. In any other case, the request will succeed and the finished event will be raised. If the application determines that the response indicates a temporary error and should be retried with the same - backoff delay that PollDance normally uses, the retry() method may be used. + backoff delay that Pollymer normally uses, the retry() method may be used. Request Reuse: diff --git a/polldance.js b/polldance.js deleted file mode 100644 index 94817f4..0000000 --- a/polldance.js +++ /dev/null @@ -1,467 +0,0 @@ -/** - * PollDance JavaScript Library v1.0.0 - * Copyright 2013 Fan Out Networks, Inc. - * Released under the MIT license (see COPYING file in source distribution) - */ -(function () { -"use strict"; -var DEBUG = true; -(function (window, undefined) { - - var NAMESPACE = "PollDance"; - var TIMEOUT = 60000; - var emptyMethod = function () { }; - - var consoleInfo; - var consoleError; - if (DEBUG) { - // don't break if there's no console - if (typeof (window.console) === "undefined") { - window.console = { info: emptyMethod, error: emptyMethod }; - } - consoleInfo = function (output) { window.console.info(output); }; - consoleError = function (output) { window.console.error(output); }; - } else { - consoleInfo = emptyMethod; - consoleError = emptyMethod; - } - - 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 parseResponseHeaders = function (headerStr) { - var headers = {}; - if (!headerStr) { - return headers; - } - var headerPairs = headerStr.split('\u000d\u000a'); - for (var i = 0; i < headerPairs.length; i++) { - var headerPair = headerPairs[i]; - var index = headerPair.indexOf('\u003a\u0020'); - if (index > 0) { - var key = headerPair.substring(0, index); - headers[key] = headerPair.substring(index + 2); - } - } - return headers; - }; - - var addJsonpScriptToDom = function (src, scriptId) { - var script = window.document.createElement("script"); - script.type = "text/javascript"; - script.id = scriptId; - script.src = src; - - var head = window.document.getElementsByTagName("head")[0]; - head.appendChild(script); - }; - var removeJsonpScriptFromDom = function (scriptId) { - var script = window.document.getElementById(scriptId); - script.parentNode.removeChild(script); - }; - - var jsonCallbacks = { - id: 0, - requests: {}, - getJsonpCallback: function (id) { - var cb; - var requests = this.requests; - if (id in this.requests) { - cb = function (result) { requests[id]._jsonpCallback(result); }; - } else { - consoleInfo("no callback with id " + id); - cb = emptyMethod; - } - return cb; - }, - addJsonpCallback: function (id, obj) { - this.requests[id] = obj; - }, - removeJsonpCallback: function (id) { - delete this.requests[id]; - }, - newCallbackInfo: function () { - var callbackInfo = { - id: "cb-" + this.id, - scriptId: "pd-jsonp-script-" + this.id - }; - this.id++; - return callbackInfo; - } - }; - - var corsAvailable = "withCredentials" in new window.XMLHttpRequest(); - - var sameOrigin = function (url) { - var loc = window.location; - 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 chooseTransport = function (transportType, url) { - var transport; - if (transportType == transportTypes.Auto) { - if (corsAvailable || sameOrigin(url)) { - transport = transportTypes.Xhr; - } else { - transport = transportTypes.Jsonp; - } - } else { - transport = transportType; - } - return transport; - }; - - var Events = function () { - this._events = {}; - }; - Events.prototype._getHandlersForType = function (type) { - if (!(type in this._events)) { - this._events[type] = []; - } - return this._events[type]; - }; - Events.prototype.on = function (type, handler) { - var handlers = this._getHandlersForType(type); - handlers.push(handler); - }; - Events.prototype.off = function (type) { - if (arguments.length > 1) { - 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 = 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 = { - "TransportError": 0, - "TimeoutError": 1 - }; - - var transportTypes = { - "Auto": 0, - "Xhr": 1, - "Jsonp": 2 - }; - - // 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 window.Error("Constructor called as a function"); - } - - this._events = new Events(); - this._tries = 0; - this._delayNext = false; - this._retryTime = 0; - this._timer = null; - this._jsonp = null; - - this._xhr = null; - this._method = null; - this._url = null; - this._headers = null; - this._body = null; - this._transport = null; - - this.transport = transportTypes.Auto; - this.rawResponse = false; - this.maxTries = 1; - this.maxDelay = 1000; - this.recurring = false; - - if (arguments.length > 0) { - var config = arguments[0]; - if ("transport" in config) { - this.transport = config.transport; - } - if ("rawResponse" in config) { - this.rawResponse = config.rawResponse; - } - if ("maxTries" in config) { - this.maxTries = config.maxTries; - } - if ("maxDelay" in config) { - this.maxDelay = config.maxDelay; - } - if ("recurring" in config) { - this.recurring = config.recurring; - } - } - }; - Request.prototype.start = function (method, url, headers, body) { - if (this._timer != null) { - consoleError("PD: start() called on a Request object that is currently running."); - return; - } - - this._method = method; - this._url = url; - this._headers = headers; - this._body = body; - this._start(); - }; - Request.prototype._start = function () { - this._tries = 0; - - var delayTime; - if (this._delayNext) { - this._delayNext = false; - delayTime = Math.floor(Math.random() * this.maxDelay); - consoleInfo("PD: polling again in " + delayTime + "ms"); - } else { - delayTime = 0; // always queue the call, to prevent browser "busy" - } - - this._initiate(delayTime); - }; - Request.prototype.retry = function () { - if (this._tries == 0) { - consoleError("PD: retry() called on a Request object that has never been started."); - return; - } - if (this._timer != null) { - consoleError("PD: retry() called on a Request object that is currently running."); - return; - } - this._retry(); - }; - Request.prototype._retry = function () { - if (this._tries === 1) { - this._retryTime = 1; - } else if (this._tries < 8) { - this._retryTime = this._retryTime * 2; - } - - var delayTime = this._retryTime * 1000; - delayTime += Math.floor(Math.random() * this.maxDelay); - consoleInfo("PD: trying again in " + delayTime + "ms"); - - this._initiate(delayTime); - }; - Request.prototype._initiate = function (delayMsecs) { - var self = this; - self._timer = window.setTimeout(function () { self._startConnect(); }, delayMsecs); - }; - Request.prototype._startConnect = function () { - var self = this; - this._timer = window.setTimeout(function () { self._timeout(); }, TIMEOUT); - - this._tries++; - - var method = this._method; - var url = (typeof (this._url) == "function") ? this._url() : this._url; - var headers = this._headers; - var body = this._body; - - // Create a copy of the transport because we don't want - // to give public access to it (changing it between now and - // cleanup would be a no-no) - this._transport = chooseTransport(this.transport, url); - - if (this._transport == transportTypes.Xhr) { - this._xhr = this._startXhr(method, url, headers, body); - } else { // Jsonp - this._jsonp = this._startJsonp(method, url, headers, body); - } - }; - Request.prototype._cleanupConnect = function (abort) { - window.clearTimeout(this._timer); - this._timer = null; - - if (this._transport == transportTypes.Xhr) { - consoleInfo("PD: XHR cleanup"); - this._cleanupXhr(this._xhr, abort); - this._xhr = null; - } else { // Jsonp - consoleInfo("PD: json-p " + this._jsonp.id + " cleanup"); - this._cleanupJsonp(this._jsonp, abort); - this._jsonp = null; - } - }; - Request.prototype.abort = function () { - this._cleanupConnect(true); - }; - Request.prototype.on = function (type, handler) { - this._events.on(type, handler); - }; - Request.prototype.off = function (type) { - var args = copyArray(arguments, 1); - args.unshift(type); - this._events.off.apply(this._events, args); - }; - Request.prototype._startXhr = function (method, url, headers, body) { - var xhr = new window.XMLHttpRequest(); - var self = this; - xhr.onreadystatechange = function () { self._xhrCallback(); }; - xhr.open(method, url, true); - - for (var key in headers) { - if (headers.hasOwnProperty(key)) { - xhr.setRequestHeader(key, headers[key]); - } - } - - xhr.send(body); - - consoleInfo("PD: XHR start " + url); - - return xhr; - }; - Request.prototype._cleanupXhr = function (xhr, abort) { - if (xhr != null) { - xhr.onreadystatechange = emptyMethod; - if (abort) { - xhr.abort(); - } - } - }; - Request.prototype._xhrCallback = function () { - var xhr = this._xhr; - if (xhr != null && xhr.readyState === 4) { - consoleInfo("PD: XHR finished"); - - var code = xhr.status; - var reason = xhr.statusText; - var headers = parseResponseHeaders(xhr.getAllResponseHeaders()); - var body = xhr.responseText; - - this._handleResponse(code, reason, headers, body); - } - }; - Request.prototype._startJsonp = function (method, url, headers, body) { - var jsonp = jsonCallbacks.newCallbackInfo(); - - var paramList = [ - "callback=" + encodeURIComponent("window['" + NAMESPACE + "']._getJsonpCallback(\"" + jsonp.id + "\")") - ]; - - if (method != "GET") { - paramList.push("_method=" + encodeURIComponent(method)); - } - if (headers) { - paramList.push("_headers=" + encodeURIComponent(JSON.stringify(headers))); - } - if (body) { - paramList.push("_body=" + encodeURIComponent(body)); - } - var params = paramList.join("&"); - - var src = (url.indexOf("?") != -1) ? url + "&" + params : url + "?" + params; - - jsonCallbacks.addJsonpCallback(jsonp.id, this); - addJsonpScriptToDom(src, jsonp.scriptId); - - consoleInfo("PD: json-p start " + jsonp.id + " " + src); - - return jsonp; - }; - Request.prototype._cleanupJsonp = function (jsonp, abort) { - if (jsonp != null) { - jsonCallbacks.removeJsonpCallback(jsonp.id); - removeJsonpScriptFromDom(jsonp.scriptId); - } - }; - Request.prototype._jsonpCallback = function (result) { - consoleInfo("PD: json-p " + this._jsonp.id + " finished"); - - var code = ("code" in result) ? result.code : 0; - var reason = ("reason" in result) ? result.reason : null; - var headers = ("headers" in result) ? result.headers : {}; - var body = ("body" in result) ? result.body : null; - - this._handleResponse(code, reason, headers, body); - }; - Request.prototype._handleResponse = function (code, reason, headers, body) { - this._cleanupConnect(); - - if ((code == 0 || (code >= 500 && code < 600)) && - (this.maxTries == -1 || this._tries < this.maxTries)) { - this._retry(); - } else { - if (code > 0) { - var result; - if (this.rawResponse) { - result = body; - } else { - try { - result = JSON.parse(body); - } catch (e) { - result = body; - } - } - this._finished(code, result, headers); - if (this.recurring && code >= 200 && code < 300) { - this._start(); - } - } else { - this._error(errorTypes.TransportError); - } - } - }; - Request.prototype._timeout = function () { - this._cleanupConnect(true); - - if (this.maxTries == -1 || this._tries < this.maxTries) { - this._retry(); - } else { - this._error(errorTypes.TimeoutError); - } - }; - Request.prototype._finished = function (code, result, headers) { - this._delayNext = true; - this._events.trigger('finished', this, code, result, headers); - }; - Request.prototype._error = function (reason) { - this._delayNext = true; - this._events.trigger('error', this, reason); - }; - - var exports = { - Request: Request, - TransportTypes: transportTypes, - ErrorTypes: errorTypes, - _getJsonpCallback: function (id) { - return jsonCallbacks.getJsonpCallback(id); - } - }; - window[NAMESPACE] = window[NAMESPACE] || exports; - -})(window); -})(); diff --git a/pollymer.js b/pollymer.js new file mode 100644 index 0000000..d4cd078 --- /dev/null +++ b/pollymer.js @@ -0,0 +1,467 @@ +/** + * Pollymer JavaScript Library v1.0.0 + * Copyright 2013 Fan Out Networks, Inc. + * Released under the MIT license (see COPYING file in source distribution) + */ +(function () { +"use strict"; +var DEBUG = true; +(function (window, undefined) { + + var NAMESPACE = "Pollymer"; + var TIMEOUT = 60000; + var emptyMethod = function () { }; + + var consoleInfo; + var consoleError; + if (DEBUG) { + // don't break if there's no console + if (typeof (window.console) === "undefined") { + window.console = { info: emptyMethod, error: emptyMethod }; + } + consoleInfo = function (output) { window.console.info(output); }; + consoleError = function (output) { window.console.error(output); }; + } else { + consoleInfo = emptyMethod; + consoleError = emptyMethod; + } + + 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 parseResponseHeaders = function (headerStr) { + var headers = {}; + if (!headerStr) { + return headers; + } + var headerPairs = headerStr.split('\u000d\u000a'); + for (var i = 0; i < headerPairs.length; i++) { + var headerPair = headerPairs[i]; + var index = headerPair.indexOf('\u003a\u0020'); + if (index > 0) { + var key = headerPair.substring(0, index); + headers[key] = headerPair.substring(index + 2); + } + } + return headers; + }; + + var addJsonpScriptToDom = function (src, scriptId) { + var script = window.document.createElement("script"); + script.type = "text/javascript"; + script.id = scriptId; + script.src = src; + + var head = window.document.getElementsByTagName("head")[0]; + head.appendChild(script); + }; + var removeJsonpScriptFromDom = function (scriptId) { + var script = window.document.getElementById(scriptId); + script.parentNode.removeChild(script); + }; + + var jsonCallbacks = { + id: 0, + requests: {}, + getJsonpCallback: function (id) { + var cb; + var requests = this.requests; + if (id in this.requests) { + cb = function (result) { requests[id]._jsonpCallback(result); }; + } else { + consoleInfo("no callback with id " + id); + cb = emptyMethod; + } + return cb; + }, + addJsonpCallback: function (id, obj) { + this.requests[id] = obj; + }, + removeJsonpCallback: function (id) { + delete this.requests[id]; + }, + newCallbackInfo: function () { + var callbackInfo = { + id: "cb-" + this.id, + scriptId: "pd-jsonp-script-" + this.id + }; + this.id++; + return callbackInfo; + } + }; + + var corsAvailable = "withCredentials" in new window.XMLHttpRequest(); + + var sameOrigin = function (url) { + var loc = window.location; + 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 chooseTransport = function (transportType, url) { + var transport; + if (transportType == transportTypes.Auto) { + if (corsAvailable || sameOrigin(url)) { + transport = transportTypes.Xhr; + } else { + transport = transportTypes.Jsonp; + } + } else { + transport = transportType; + } + return transport; + }; + + var Events = function () { + this._events = {}; + }; + Events.prototype._getHandlersForType = function (type) { + if (!(type in this._events)) { + this._events[type] = []; + } + return this._events[type]; + }; + Events.prototype.on = function (type, handler) { + var handlers = this._getHandlersForType(type); + handlers.push(handler); + }; + Events.prototype.off = function (type) { + if (arguments.length > 1) { + 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 = 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 = { + "TransportError": 0, + "TimeoutError": 1 + }; + + var transportTypes = { + "Auto": 0, + "Xhr": 1, + "Jsonp": 2 + }; + + // Pollymer.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 window.Error("Constructor called as a function"); + } + + this._events = new Events(); + this._tries = 0; + this._delayNext = false; + this._retryTime = 0; + this._timer = null; + this._jsonp = null; + + this._xhr = null; + this._method = null; + this._url = null; + this._headers = null; + this._body = null; + this._transport = null; + + this.transport = transportTypes.Auto; + this.rawResponse = false; + this.maxTries = 1; + this.maxDelay = 1000; + this.recurring = false; + + if (arguments.length > 0) { + var config = arguments[0]; + if ("transport" in config) { + this.transport = config.transport; + } + if ("rawResponse" in config) { + this.rawResponse = config.rawResponse; + } + if ("maxTries" in config) { + this.maxTries = config.maxTries; + } + if ("maxDelay" in config) { + this.maxDelay = config.maxDelay; + } + if ("recurring" in config) { + this.recurring = config.recurring; + } + } + }; + Request.prototype.start = function (method, url, headers, body) { + if (this._timer != null) { + consoleError("pollymer: start() called on a Request object that is currently running."); + return; + } + + this._method = method; + this._url = url; + this._headers = headers; + this._body = body; + this._start(); + }; + Request.prototype._start = function () { + this._tries = 0; + + var delayTime; + if (this._delayNext) { + this._delayNext = false; + delayTime = Math.floor(Math.random() * this.maxDelay); + consoleInfo("pollymer: polling again in " + delayTime + "ms"); + } else { + delayTime = 0; // always queue the call, to prevent browser "busy" + } + + this._initiate(delayTime); + }; + Request.prototype.retry = function () { + if (this._tries == 0) { + consoleError("pollymer: retry() called on a Request object that has never been started."); + return; + } + if (this._timer != null) { + consoleError("pollymer: retry() called on a Request object that is currently running."); + return; + } + this._retry(); + }; + Request.prototype._retry = function () { + if (this._tries === 1) { + this._retryTime = 1; + } else if (this._tries < 8) { + this._retryTime = this._retryTime * 2; + } + + var delayTime = this._retryTime * 1000; + delayTime += Math.floor(Math.random() * this.maxDelay); + consoleInfo("pollymer: trying again in " + delayTime + "ms"); + + this._initiate(delayTime); + }; + Request.prototype._initiate = function (delayMsecs) { + var self = this; + self._timer = window.setTimeout(function () { self._startConnect(); }, delayMsecs); + }; + Request.prototype._startConnect = function () { + var self = this; + this._timer = window.setTimeout(function () { self._timeout(); }, TIMEOUT); + + this._tries++; + + var method = this._method; + var url = (typeof (this._url) == "function") ? this._url() : this._url; + var headers = this._headers; + var body = this._body; + + // Create a copy of the transport because we don't want + // to give public access to it (changing it between now and + // cleanup would be a no-no) + this._transport = chooseTransport(this.transport, url); + + if (this._transport == transportTypes.Xhr) { + this._xhr = this._startXhr(method, url, headers, body); + } else { // Jsonp + this._jsonp = this._startJsonp(method, url, headers, body); + } + }; + Request.prototype._cleanupConnect = function (abort) { + window.clearTimeout(this._timer); + this._timer = null; + + if (this._transport == transportTypes.Xhr) { + consoleInfo("pollymer: XHR cleanup"); + this._cleanupXhr(this._xhr, abort); + this._xhr = null; + } else { // Jsonp + consoleInfo("pollymer: json-p " + this._jsonp.id + " cleanup"); + this._cleanupJsonp(this._jsonp, abort); + this._jsonp = null; + } + }; + Request.prototype.abort = function () { + this._cleanupConnect(true); + }; + Request.prototype.on = function (type, handler) { + this._events.on(type, handler); + }; + Request.prototype.off = function (type) { + var args = copyArray(arguments, 1); + args.unshift(type); + this._events.off.apply(this._events, args); + }; + Request.prototype._startXhr = function (method, url, headers, body) { + var xhr = new window.XMLHttpRequest(); + var self = this; + xhr.onreadystatechange = function () { self._xhrCallback(); }; + xhr.open(method, url, true); + + for (var key in headers) { + if (headers.hasOwnProperty(key)) { + xhr.setRequestHeader(key, headers[key]); + } + } + + xhr.send(body); + + consoleInfo("pollymer: XHR start " + url); + + return xhr; + }; + Request.prototype._cleanupXhr = function (xhr, abort) { + if (xhr != null) { + xhr.onreadystatechange = emptyMethod; + if (abort) { + xhr.abort(); + } + } + }; + Request.prototype._xhrCallback = function () { + var xhr = this._xhr; + if (xhr != null && xhr.readyState === 4) { + consoleInfo("pollymer: XHR finished"); + + var code = xhr.status; + var reason = xhr.statusText; + var headers = parseResponseHeaders(xhr.getAllResponseHeaders()); + var body = xhr.responseText; + + this._handleResponse(code, reason, headers, body); + } + }; + Request.prototype._startJsonp = function (method, url, headers, body) { + var jsonp = jsonCallbacks.newCallbackInfo(); + + var paramList = [ + "callback=" + encodeURIComponent("window['" + NAMESPACE + "']._getJsonpCallback(\"" + jsonp.id + "\")") + ]; + + if (method != "GET") { + paramList.push("_method=" + encodeURIComponent(method)); + } + if (headers) { + paramList.push("_headers=" + encodeURIComponent(JSON.stringify(headers))); + } + if (body) { + paramList.push("_body=" + encodeURIComponent(body)); + } + var params = paramList.join("&"); + + var src = (url.indexOf("?") != -1) ? url + "&" + params : url + "?" + params; + + jsonCallbacks.addJsonpCallback(jsonp.id, this); + addJsonpScriptToDom(src, jsonp.scriptId); + + consoleInfo("pollymer: json-p start " + jsonp.id + " " + src); + + return jsonp; + }; + Request.prototype._cleanupJsonp = function (jsonp, abort) { + if (jsonp != null) { + jsonCallbacks.removeJsonpCallback(jsonp.id); + removeJsonpScriptFromDom(jsonp.scriptId); + } + }; + Request.prototype._jsonpCallback = function (result) { + consoleInfo("pollymer: json-p " + this._jsonp.id + " finished"); + + var code = ("code" in result) ? result.code : 0; + var reason = ("reason" in result) ? result.reason : null; + var headers = ("headers" in result) ? result.headers : {}; + var body = ("body" in result) ? result.body : null; + + this._handleResponse(code, reason, headers, body); + }; + Request.prototype._handleResponse = function (code, reason, headers, body) { + this._cleanupConnect(); + + if ((code == 0 || (code >= 500 && code < 600)) && + (this.maxTries == -1 || this._tries < this.maxTries)) { + this._retry(); + } else { + if (code > 0) { + var result; + if (this.rawResponse) { + result = body; + } else { + try { + result = JSON.parse(body); + } catch (e) { + result = body; + } + } + this._finished(code, result, headers); + if (this.recurring && code >= 200 && code < 300) { + this._start(); + } + } else { + this._error(errorTypes.TransportError); + } + } + }; + Request.prototype._timeout = function () { + this._cleanupConnect(true); + + if (this.maxTries == -1 || this._tries < this.maxTries) { + this._retry(); + } else { + this._error(errorTypes.TimeoutError); + } + }; + Request.prototype._finished = function (code, result, headers) { + this._delayNext = true; + this._events.trigger('finished', this, code, result, headers); + }; + Request.prototype._error = function (reason) { + this._delayNext = true; + this._events.trigger('error', this, reason); + }; + + var exports = { + Request: Request, + TransportTypes: transportTypes, + ErrorTypes: errorTypes, + _getJsonpCallback: function (id) { + return jsonCallbacks.getJsonpCallback(id); + } + }; + window[NAMESPACE] = window[NAMESPACE] || exports; + +})(window); +})(); -- cgit v1.2.3