From a7f3b2b6674424e7ab356fa7f1778fecc36af7e4 Mon Sep 17 00:00:00 2001 From: Donghwan Kim Date: Wed, 31 Jul 2013 17:37:08 +0900 Subject: [PATCH] Update atmosphere.js to 2.0.0.RC4 --- ajax/libs/atmosphere/2.0.0.RC4/atmosphere.js | 2855 +++++++++++++++++ .../atmosphere/2.0.0.RC4/atmosphere.min.js | 16 + ajax/libs/atmosphere/package.json | 2 +- 3 files changed, 2872 insertions(+), 1 deletion(-) create mode 100644 ajax/libs/atmosphere/2.0.0.RC4/atmosphere.js create mode 100644 ajax/libs/atmosphere/2.0.0.RC4/atmosphere.min.js diff --git a/ajax/libs/atmosphere/2.0.0.RC4/atmosphere.js b/ajax/libs/atmosphere/2.0.0.RC4/atmosphere.js new file mode 100644 index 000000000..3391b5309 --- /dev/null +++ b/ajax/libs/atmosphere/2.0.0.RC4/atmosphere.js @@ -0,0 +1,2855 @@ +/** + * Copyright 2012 Jeanfrancois Arcand + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Highly inspired by Portal v1.0 + * http://github.com/flowersinthesand/portal + * + * Copyright 2011-2013, Donghwan Kim + * Licensed under the Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + */ +/** + * Official documentation of this library: https://github.com/Atmosphere/atmosphere/wiki/jQuery.atmosphere.js-API + */ +(function() { + + "use strict"; + + var version = "2.0.0-javascript", + atmosphere = {}, + guid, + requests = [], + callbacks = [], + hasOwn = Object.prototype.hasOwnProperty; + + atmosphere = { + + onError: function(response) {}, + onClose: function(response) {}, + onOpen: function(response) {}, + onReopen: function(response) {}, + onMessage: function(response) {}, + onReconnect: function(request, response) {}, + onMessagePublished: function(response) {}, + onTransportFailure: function(errorMessage, _request) {}, + onLocalMessage: function(response) {}, + onFailureToReconnect: function(request, response) {}, + + AtmosphereRequest: function(options) { + + /** + * {Object} Request parameters. + * + * @private + */ + var _request = { + timeout: 300000, + method: 'GET', + headers: {}, + contentType: '', + callback: null, + url: '', + data: '', + suspend: true, + maxRequest: -1, + reconnect: true, + maxStreamingLength: 10000000, + lastIndex: 0, + logLevel: 'info', + requestCount: 0, + fallbackMethod: 'GET', + fallbackTransport: 'streaming', + transport: 'long-polling', + webSocketImpl: null, + webSocketBinaryType: null, + dispatchUrl: null, + webSocketPathDelimiter: "@@", + enableXDR: false, + rewriteURL: false, + attachHeadersAsQueryString: true, + executeCallbackBeforeReconnect: false, + readyState: 0, + lastTimestamp: 0, + withCredentials: false, + trackMessageLength: false, + messageDelimiter: '|', + connectTimeout: -1, + reconnectInterval: 0, + dropAtmosphereHeaders: true, + uuid: 0, + async: true, + shared: false, + readResponsesHeaders: false, + maxReconnectOnClose: 5, + enableProtocol: true, + onError: function(response) {}, + onClose: function(response) {}, + onOpen: function(response) {}, + onMessage: function(response) {}, + onReopen: function(request, response) {}, + onReconnect: function(request, response) {}, + onMessagePublished: function(response) {}, + onTransportFailure: function(reason, request) {}, + onLocalMessage: function(request) {}, + onFailureToReconnect: function(request, response) {} + }; + + /** + * {Object} Request's last response. + * + * @private + */ + var _response = { + status: 200, + reasonPhrase: "OK", + responseBody: '', + messages: [], + headers: [], + state: "messageReceived", + transport: "polling", + error: null, + request: null, + partialMessage: "", + errorHandled: false, + id: 0 + }; + + /** + * {websocket} Opened web socket. + * + * @private + */ + var _websocket = null; + + /** + * {SSE} Opened SSE. + * + * @private + */ + var _sse = null; + + /** + * {XMLHttpRequest, ActiveXObject} Opened ajax request (in case of http-streaming or long-polling) + * + * @private + */ + var _activeRequest = null; + + /** + * {Object} Object use for streaming with IE. + * + * @private + */ + var _ieStream = null; + + /** + * {Object} Object use for jsonp transport. + * + * @private + */ + var _jqxhr = null; + + /** + * {boolean} If request has been subscribed or not. + * + * @private + */ + var _subscribed = true; + + /** + * {number} Number of test reconnection. + * + * @private + */ + var _requestCount = 0; + + /** + * {boolean} If request is currently aborded. + * + * @private + */ + var _abordingConnection = false; + + /** + * A local "channel' of communication. + * + * @private + */ + var _localSocketF = null; + + /** + * The storage used. + * + * @private + */ + var _storageService; + + /** + * Local communication + * + * @private + */ + var _localStorageService = null; + + /** + * A Unique ID + * + * @private + */ + var guid = atmosphere.util.now(); + + /** Trace time */ + var _traceTimer; + + // Automatic call to subscribe + _subscribe(options); + + /** + * Initialize atmosphere request object. + * + * @private + */ + function _init() { + _subscribed = true; + _abordingConnection = false; + _requestCount = 0; + + _websocket = null; + _sse = null; + _activeRequest = null; + _ieStream = null; + } + + /** + * Re-initialize atmosphere object. + * + * @private + */ + function _reinit() { + _clearState(); + _init(); + } + + /** + * + * @private + */ + function _verifyStreamingLength(ajaxRequest, rq) { + // Wait to be sure we have the full message before closing. + if (_response.partialMessage === "" && (rq.transport === 'streaming') && (ajaxRequest.responseText.length > rq.maxStreamingLength)) { + _response.messages = []; + _invokeClose(true); + _disconnect(); + _clearState(); + _reconnect(ajaxRequest, rq, 0); + } + } + + /** + * Disconnect + * + * @private + */ + function _disconnect() { + if (_request.enableProtocol && !_request.firstMessage) { + var query = "X-Atmosphere-Transport=close&X-Atmosphere-tracking-id=" + _request.uuid; + var url = _request.url.replace(/([?&])_=[^&]*/, query); + url = url + (url === _request.url ? (/\?/.test(_request.url) ? "&" : "?") + query : ""); + _request.attachHeadersAsQueryString = false; + _request.dropAtmosphereHeaders = true; + _request.url = url; + _request.transport = 'polling'; + _pushOnClose("", _request); + } + } + + /** + * Close request. + * + * @private + */ + function _close() { + _request.reconnect = false; + _abordingConnection = true; + _response.request = _request; + _response.state = 'unsubscribe'; + _response.responseBody = ""; + _response.status = 408; + _invokeCallback(); + _disconnect(); + _clearState(); + } + + function _clearState() { + if (_request.id) { + clearTimeout(_request.id); + } + + if (_ieStream != null) { + _ieStream.close(); + _ieStream = null; + } + if (_jqxhr != null) { + _jqxhr.abort(); + _jqxhr = null; + } + if (_activeRequest != null) { + _activeRequest.abort(); + _activeRequest = null; + } + if (_websocket != null) { + if (_websocket.webSocketOpened) { + _websocket.close(); + } + _websocket = null; + } + if (_sse != null) { + _sse.close(); + _sse = null; + } + _clearStorage(); + } + + function _clearStorage() { + // Stop sharing a connection + if (_storageService != null) { + // Clears trace timer + clearInterval(_traceTimer); + // Removes the trace + document.cookie = encodeURIComponent("atmosphere-" + _request.url) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT"; + // The heir is the parent unless unloading + _storageService.signal("close", { + reason: "", + heir: !_abordingConnection ? guid : (_storageService.get("children") || [])[0] + }); + _storageService.close(); + } + if (_localStorageService != null) { + _localStorageService.close(); + } + } + + /** + * Subscribe request using request transport.
+ * If request is currently opened, this one will be closed. + * + * @param {Object} Request parameters. + * @private + */ + function _subscribe(options) { + _reinit(); + + _request = atmosphere.util.extend(_request, options); + // Allow at least 1 request + _request.mrequest = _request.reconnect; + if (!_request.reconnect) { + _request.reconnect = true; + } + } + + /** + * Check if web socket is supported (check for custom implementation provided by request object or browser implementation). + * + * @returns {boolean} True if web socket is supported, false otherwise. + * @private + */ + function _supportWebsocket() { + return _request.webSocketImpl != null || window.WebSocket || window.MozWebSocket; + } + + /** + * Check if server side events (SSE) is supported (check for custom implementation provided by request object or browser implementation). + * + * @returns {boolean} True if web socket is supported, false otherwise. + * @private + */ + function _supportSSE() { + return window.EventSource; + } + + /** + * Open request using request transport.
+ * If request transport is 'websocket' but websocket can't be opened, request will automatically reconnect using fallback transport. + * + * @private + */ + function _execute() { + // Shared across multiple tabs/windows. + if (_request.shared) { + _localStorageService = _local(_request); + if (_localStorageService != null) { + if (_request.logLevel === 'debug') { + atmosphere.util.debug("Storage service available. All communication will be local"); + } + + if (_localStorageService.open(_request)) { + // Local connection. + return; + } + } + + if (_request.logLevel === 'debug') { + atmosphere.util.debug("No Storage service available."); + } + // Wasn't local or an error occurred + _localStorageService = null; + } + + // Protocol + _request.firstMessage = true; + _request.isOpen = false; + _request.ctime = atmosphere.util.now(); + + if (_request.transport !== 'websocket' && _request.transport !== 'sse') { + _executeRequest(_request); + + } else if (_request.transport === 'websocket') { + if (!_supportWebsocket()) { + _reconnectWithFallbackTransport("Websocket is not supported, using request.fallbackTransport (" + _request.fallbackTransport + + ")"); + } else { + _executeWebSocket(false); + } + } else if (_request.transport === 'sse') { + if (!_supportSSE()) { + _reconnectWithFallbackTransport("Server Side Events(SSE) is not supported, using request.fallbackTransport (" + + _request.fallbackTransport + ")"); + } else { + _executeSSE(false); + } + } + } + + function _local(request) { + var trace, connector, orphan, name = "atmosphere-" + request.url, connectors = { + storage: function() { + if (!atmosphere.util.supportStorage()) { + return; + } + + var storage = window.localStorage, get = function(key) { + return atmosphere.util.parseJSON(storage.getItem(name + "-" + key)); + }, set = function(key, value) { + storage.setItem(name + "-" + key, atmosphere.util.stringifyJSON(value)); + }; + + return { + init: function() { + set("children", get("children").concat([guid])); + atmosphere.util.on("storage.socket", function(event) { + event = event.originalEvent; + if (event.key === name && event.newValue) { + listener(event.newValue); + } + }); + return get("opened"); + }, + signal: function(type, data) { + storage.setItem(name, atmosphere.util.stringifyJSON({ + target: "p", + type: type, + data: data + })); + }, + close: function() { + var children = get("children"); + + atmosphere.util.off("storage.socket"); + if (children) { + if (removeFromArray(children, request.id)) { + set("children", children); + } + } + } + }; + }, + windowref: function() { + var win = window.open("", name.replace(/\W/g, "")); + + if (!win || win.closed || !win.callbacks) { + return; + } + + return { + init: function() { + win.callbacks.push(listener); + win.children.push(guid); + return win.opened; + }, + signal: function(type, data) { + if (!win.closed && win.fire) { + win.fire(atmosphere.util.stringifyJSON({ + target: "p", + type: type, + data: data + })); + } + }, + close: function() { + // Removes traces only if the parent is alive + if (!orphan) { + removeFromArray(win.callbacks, listener); + removeFromArray(win.children, guid); + } + } + + }; + } + }; + + function removeFromArray(array, val) { + var i, length = array.length; + + for (i = 0; i < length; i++) { + if (array[i] === val) { + array.splice(i, 1); + } + } + + return length !== array.length; + } + + // Receives open, close and message command from the parent + function listener(string) { + var command = atmosphere.util.parseJSON(string), data = command.data; + + if (command.target === "c") { + switch (command.type) { + case "open": + _open("opening", 'local', _request); + break; + case "close": + if (!orphan) { + orphan = true; + if (data.reason === "aborted") { + _close(); + } else { + // Gives the heir some time to reconnect + if (data.heir === guid) { + _execute(); + } else { + setTimeout(function() { + _execute(); + }, 100); + } + } + } + break; + case "message": + _prepareCallback(data, "messageReceived", 200, request.transport); + break; + case "localMessage": + _localMessage(data); + break; + } + } + } + + function findTrace() { + var matcher = new RegExp("(?:^|; )(" + encodeURIComponent(name) + ")=([^;]*)").exec(document.cookie); + if (matcher) { + return atmosphere.util.parseJSON(decodeURIComponent(matcher[2])); + } + } + + // Finds and validates the parent socket's trace from the cookie + trace = findTrace(); + if (!trace || atmosphere.util.now() - trace.ts > 1000) { + return; + } + + // Chooses a connector + connector = connectors.storage() || connectors.windowref(); + if (!connector) { + return; + } + + return { + open: function() { + var parentOpened; + + // Checks the shared one is alive + _traceTimer = setInterval(function() { + var oldTrace = trace; + trace = findTrace(); + if (!trace || oldTrace.ts === trace.ts) { + // Simulates a close signal + listener(atmosphere.util.stringifyJSON({ + target: "c", + type: "close", + data: { + reason: "error", + heir: oldTrace.heir + } + })); + } + }, 1000); + + parentOpened = connector.init(); + if (parentOpened) { + // Firing the open event without delay robs the user of the opportunity to bind connecting event handlers + setTimeout(function() { + _open("opening", 'local', request); + }, 50); + } + return parentOpened; + }, + send: function(event) { + connector.signal("send", event); + }, + localSend: function(event) { + connector.signal("localSend", atmosphere.util.stringifyJSON({ + id: guid, + event: event + })); + }, + close: function() { + // Do not signal the parent if this method is executed by the unload event handler + if (!_abordingConnection) { + clearInterval(_traceTimer); + connector.signal("close"); + connector.close(); + } + } + }; + } + + function share() { + var storageService, name = "atmosphere-" + _request.url, servers = { + // Powered by the storage event and the localStorage + // http://www.w3.org/TR/webstorage/#event-storage + storage: function() { + if (!atmosphere.util.supportStorage()) { + return; + } + + var storage = window.localStorage; + + return { + init: function() { + // Handles the storage event + atmosphere.util.on("storage.socket", function(event) { + event = event.originalEvent; + // When a deletion, newValue initialized to null + if (event.key === name && event.newValue) { + listener(event.newValue); + } + }); + }, + signal: function(type, data) { + storage.setItem(name, atmosphere.util.stringifyJSON({ + target: "c", + type: type, + data: data + })); + }, + get: function(key) { + return atmosphere.util.parseJSON(storage.getItem(name + "-" + key)); + }, + set: function(key, value) { + storage.setItem(name + "-" + key, atmosphere.util.stringifyJSON(value)); + }, + close: function() { + atmosphere.util.off("storage.socket"); + storage.removeItem(name); + storage.removeItem(name + "-opened"); + storage.removeItem(name + "-children"); + } + + }; + }, + // Powered by the window.open method + // https://developer.mozilla.org/en/DOM/window.open + windowref: function() { + // Internet Explorer raises an invalid argument error + // when calling the window.open method with the name containing non-word characters + var neim = name.replace(/\W/g, ""), container = document.getElementById(neim), win; + + if (!container) { + container = document.createElement("div"); + container.id = neim; + container.style.display = "none"; + container.innerHTML = '