diff options
-rw-r--r-- | plugins/Orbited/bayeux.class.inc.php | 134 | ||||
-rw-r--r-- | plugins/Orbited/jquery.comet.js | 1451 | ||||
-rw-r--r-- | plugins/Orbited/json2.js | 478 |
3 files changed, 0 insertions, 2063 deletions
diff --git a/plugins/Orbited/bayeux.class.inc.php b/plugins/Orbited/bayeux.class.inc.php deleted file mode 100644 index 39ad8a8fc..000000000 --- a/plugins/Orbited/bayeux.class.inc.php +++ /dev/null @@ -1,134 +0,0 @@ -<?php -/* - * - * Phomet: a php comet client - * - * Copyright (C) 2008 Morgan 'ARR!' Allen <morganrallen@gmail.com> http://morglog.alleycatracing.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -class Bayeux -{ - private $oCurl = ''; - private $nNextId = 0; - - private $sUser = ''; - private $sPassword = ''; - - public $sUrl = ''; - - function __construct($sUrl, $sUser='', $sPassword='') - { - $this->sUrl = $sUrl; - - $this->oCurl = curl_init(); - - $aHeaders = array(); - $aHeaders[] = 'Connection: Keep-Alive'; - - curl_setopt($this->oCurl, CURLOPT_URL, $sUrl); - curl_setopt($this->oCurl, CURLOPT_HTTPHEADER, $aHeaders); - curl_setopt($this->oCurl, CURLOPT_HEADER, 0); - curl_setopt($this->oCurl, CURLOPT_POST, 1); - curl_setopt($this->oCurl, CURLOPT_RETURNTRANSFER,1); - - if (!is_null($sUser) && mb_strlen($sUser) > 0) { - curl_setopt($this->oCurl, CURLOPT_USERPWD,"$sUser:$sPassword"); - } - - $this->handShake(); - } - - function __destruct() - { - $this->disconnect(); - } - - function handShake() - { - $msgHandshake = array(); - $msgHandshake['channel'] = '/meta/handshake'; - $msgHandshake['version'] = "1.0"; - $msgHandshake['minimumVersion'] = "0.9"; - $msgHandshake['supportedConnectionTypes'] = array('long-polling'); - $msgHandshake['id'] = $this->nNextId++; - - curl_setopt($this->oCurl, CURLOPT_POSTFIELDS, "message=".urlencode(str_replace('\\', '', json_encode(array($msgHandshake))))); - - $data = curl_exec($this->oCurl); - - if(curl_errno($this->oCurl)) - die("Error: " . curl_error($this->oCurl)); - - $oReturn = json_decode($data); - - if (is_array($oReturn)) { - $oReturn = $oReturn[0]; - } - - $bSuccessful = ($oReturn->successful) ? true : false; - - if($bSuccessful) - { - $this->clientId = $oReturn->clientId; - - $this->connect(); - } - } - - public function connect() - { - $aMsg['channel'] = '/meta/connect'; - $aMsg['id'] = $this->nNextId++; - $aMsg['clientId'] = $this->clientId; - $aMsg['connectionType'] = 'long-polling'; - - curl_setopt($this->oCurl, CURLOPT_POSTFIELDS, "message=".urlencode(str_replace('\\', '', json_encode(array($aMsg))))); - - $data = curl_exec($this->oCurl); - } - - function disconnect() - { - $msgHandshake = array(); - $msgHandshake['channel'] = '/meta/disconnect'; - $msgHandshake['id'] = $this->nNextId++; - $msgHandshake['clientId'] = $this->clientId; - - curl_setopt($this->oCurl, CURLOPT_POSTFIELDS, "message=".urlencode(str_replace('\\', '', json_encode(array($msgHandshake))))); - - curl_exec($this->oCurl); - } - - public function publish($sChannel, $oData) - { - if(!$sChannel || !$oData) - return; - - $aMsg = array(); - - $aMsg['channel'] = $sChannel; - $aMsg['id'] = $this->nNextId++; - $aMsg['data'] = $oData; - $aMsg['clientId'] = $this->clientId; - - curl_setopt($this->oCurl, CURLOPT_POSTFIELDS, "message=".urlencode(str_replace('\\', '', json_encode(array($aMsg))))); - - $data = curl_exec($this->oCurl); -// var_dump($data); - } -} diff --git a/plugins/Orbited/jquery.comet.js b/plugins/Orbited/jquery.comet.js deleted file mode 100644 index 6de437fa8..000000000 --- a/plugins/Orbited/jquery.comet.js +++ /dev/null @@ -1,1451 +0,0 @@ -/** - * Copyright 2008 Mort Bay Consulting Pty. Ltd. - * Dual licensed under the Apache License 2.0 and the MIT license. - * ---------------------------------------------------------------------------- - * 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. - * ---------------------------------------------------------------------------- - * Licensed under the MIT license; - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * ---------------------------------------------------------------------------- - * $Revision$ $Date$ - */ -(function($) -{ - /** - * The constructor for a Comet object. - * There is a default Comet instance already created at the variable <code>$.cometd</code>, - * and hence that can be used to start a comet conversation with a server. - * In the rare case a page needs more than one comet conversation, a new instance can be - * created via: - * <pre> - * var url2 = ...; - * var cometd2 = new $.Cometd(); - * cometd2.init(url2); - * </pre> - */ - $.Cometd = function(name) - { - var _name = name || 'default'; - var _logPriorities = { debug: 1, info: 2, warn: 3, error: 4 }; - var _logLevel = 'info'; - var _url; - var _xd = false; - var _transport; - var _status = 'disconnected'; - var _messageId = 0; - var _clientId = null; - var _batch = 0; - var _messageQueue = []; - var _listeners = {}; - var _backoff = 0; - var _backoffIncrement = 1000; - var _maxBackoff = 60000; - var _scheduledSend = null; - var _extensions = []; - var _advice = {}; - var _handshakeProps; - - /** - * Returns the name assigned to this Comet object, or the string 'default' - * if no name has been explicitely passed as parameter to the constructor. - */ - this.getName = function() - { - return _name; - }; - - /** - * Configures the initial comet communication with the comet server. - * @param cometURL the URL of the comet server - */ - this.configure = function(cometURL) - { - _configure(cometURL); - }; - - function _configure(cometURL) - { - _url = cometURL; - _debug('Initializing comet with url: {}', _url); - - // Check immediately if we're cross domain - // If cross domain, the handshake must not send the long polling transport type - var urlParts = /(^https?:)?(\/\/(([^:\/\?#]+)(:(\d+))?))?([^\?#]*)/.exec(cometURL); - if (urlParts[3]) _xd = urlParts[3] != location.host; - - // Temporary setup a transport to send the initial handshake - // The transport may be changed as a result of handshake - if (_xd) - _transport = newCallbackPollingTransport(); - else - _transport = newLongPollingTransport(); - _debug('Initial transport is {}', _transport.getType()); - }; - - /** - * Configures and establishes the comet communication with the comet server - * via a handshake and a subsequent connect. - * @param cometURL the URL of the comet server - * @param handshakeProps an object to be merged with the handshake message - * @see #configure(cometURL) - * @see #handshake(handshakeProps) - */ - this.init = function(cometURL, handshakeProps) - { - _configure(cometURL); - _handshake(handshakeProps); - }; - - /** - * Establishes the comet communication with the comet server - * via a handshake and a subsequent connect. - * @param handshakeProps an object to be merged with the handshake message - */ - this.handshake = function(handshakeProps) - { - _handshake(handshakeProps); - }; - - /** - * Disconnects from the comet server. - * @param disconnectProps an object to be merged with the disconnect message - */ - this.disconnect = function(disconnectProps) - { - var bayeuxMessage = { - channel: '/meta/disconnect' - }; - var message = $.extend({}, disconnectProps, bayeuxMessage); - // Deliver immediately - // The handshake and connect mechanism make use of startBatch(), and in case - // of a failed handshake the disconnect would not be delivered if using _send(). - _setStatus('disconnecting'); - _deliver([message], false); - }; - - /** - * Marks the start of a batch of application messages to be sent to the server - * in a single request, obtaining a single response containing (possibly) many - * application reply messages. - * Messages are held in a queue and not sent until {@link #endBatch()} is called. - * If startBatch() is called multiple times, then an equal number of endBatch() - * calls must be made to close and send the batch of messages. - * @see #endBatch() - */ - this.startBatch = function() - { - _startBatch(); - }; - - /** - * Marks the end of a batch of application messages to be sent to the server - * in a single request. - * @see #startBatch() - */ - this.endBatch = function() - { - _endBatch(true); - }; - - /** - * Subscribes to the given channel, performing the given callback in the given scope - * when a message for the channel arrives. - * @param channel the channel to subscribe to - * @param scope the scope of the callback - * @param callback the callback to call when a message is delivered to the channel - * @param subscribeProps an object to be merged with the subscribe message - * @return the subscription handle to be passed to {@link #unsubscribe(object)} - */ - this.subscribe = function(channel, scope, callback, subscribeProps) - { - var subscription = this.addListener(channel, scope, callback); - - // Send the subscription message after the subscription registration to avoid - // races where the server would deliver a message to the subscribers, but here - // on the client the subscription has not been added yet to the data structures - var bayeuxMessage = { - channel: '/meta/subscribe', - subscription: channel - }; - var message = $.extend({}, subscribeProps, bayeuxMessage); - _send(message); - - return subscription; - }; - - /** - * Unsubscribes the subscription obtained with a call to {@link #subscribe(string, object, function)}. - * @param subscription the subscription to unsubscribe. - */ - this.unsubscribe = function(subscription, unsubscribeProps) - { - // Remove the local listener before sending the message - // This ensures that if the server fails, this client does not get notifications - this.removeListener(subscription); - var bayeuxMessage = { - channel: '/meta/unsubscribe', - subscription: subscription[0] - }; - var message = $.extend({}, unsubscribeProps, bayeuxMessage); - _send(message); - }; - - /** - * Publishes a message on the given channel, containing the given content. - * @param channel the channel to publish the message to - * @param content the content of the message - * @param publishProps an object to be merged with the publish message - */ - this.publish = function(channel, content, publishProps) - { - var bayeuxMessage = { - channel: channel, - data: content - }; - var message = $.extend({}, publishProps, bayeuxMessage); - _send(message); - }; - - /** - * Adds a listener for bayeux messages, performing the given callback in the given scope - * when a message for the given channel arrives. - * @param channel the channel the listener is interested to - * @param scope the scope of the callback - * @param callback the callback to call when a message is delivered to the channel - * @returns the subscription handle to be passed to {@link #removeListener(object)} - * @see #removeListener(object) - */ - this.addListener = function(channel, scope, callback) - { - // The data structure is a map<channel, subscription[]>, where each subscription - // holds the callback to be called and its scope. - - // Normalize arguments - if (!callback) - { - callback = scope; - scope = undefined; - } - - var subscription = { - scope: scope, - callback: callback - }; - - var subscriptions = _listeners[channel]; - if (!subscriptions) - { - subscriptions = []; - _listeners[channel] = subscriptions; - } - // Pushing onto an array appends at the end and returns the id associated with the element increased by 1. - // Note that if: - // a.push('a'); var hb=a.push('b'); delete a[hb-1]; var hc=a.push('c'); - // then: - // hc==3, a.join()=='a',,'c', a.length==3 - var subscriptionIndex = subscriptions.push(subscription) - 1; - _debug('Added listener: channel \'{}\', callback \'{}\', index {}', channel, callback.name, subscriptionIndex); - - // The subscription to allow removal of the listener is made of the channel and the index - return [channel, subscriptionIndex]; - }; - - /** - * Removes the subscription obtained with a call to {@link #addListener(string, object, function)}. - * @param subscription the subscription to unsubscribe. - */ - this.removeListener = function(subscription) - { - var subscriptions = _listeners[subscription[0]]; - if (subscriptions) - { - delete subscriptions[subscription[1]]; - _debug('Removed listener: channel \'{}\', index {}', subscription[0], subscription[1]); - } - }; - - /** - * Removes all listeners registered with {@link #addListener(channel, scope, callback)} or - * {@link #subscribe(channel, scope, callback)}. - */ - this.clearListeners = function() - { - _listeners = {}; - }; - - /** - * Returns a string representing the status of the bayeux communication with the comet server. - */ - this.getStatus = function() - { - return _status; - }; - - /** - * Sets the backoff period used to increase the backoff time when retrying an unsuccessful or failed message. - * Default value is 1 second, which means if there is a persistent failure the retries will happen - * after 1 second, then after 2 seconds, then after 3 seconds, etc. So for example with 15 seconds of - * elapsed time, there will be 5 retries (at 1, 3, 6, 10 and 15 seconds elapsed). - * @param period the backoff period to set - * @see #getBackoffIncrement() - */ - this.setBackoffIncrement = function(period) - { - _backoffIncrement = period; - }; - - /** - * Returns the backoff period used to increase the backoff time when retrying an unsuccessful or failed message. - * @see #setBackoffIncrement(period) - */ - this.getBackoffIncrement = function() - { - return _backoffIncrement; - }; - - /** - * Returns the backoff period to wait before retrying an unsuccessful or failed message. - */ - this.getBackoffPeriod = function() - { - return _backoff; - }; - - /** - * Sets the log level for console logging. - * Valid values are the strings 'error', 'warn', 'info' and 'debug', from - * less verbose to more verbose. - * @param level the log level string - */ - this.setLogLevel = function(level) - { - _logLevel = level; - }; - - /** - * Registers an extension whose callbacks are called for every incoming message - * (that comes from the server to this client implementation) and for every - * outgoing message (that originates from this client implementation for the - * server). - * The format of the extension object is the following: - * <pre> - * { - * incoming: function(message) { ... }, - * outgoing: function(message) { ... } - * } - * Both properties are optional, but if they are present they will be called - * respectively for each incoming message and for each outgoing message. - * </pre> - * @param name the name of the extension - * @param extension the extension to register - * @return true if the extension was registered, false otherwise - * @see #unregisterExtension(name) - */ - this.registerExtension = function(name, extension) - { - var existing = false; - for (var i = 0; i < _extensions.length; ++i) - { - var existingExtension = _extensions[i]; - if (existingExtension.name == name) - { - existing = true; - return false; - } - } - if (!existing) - { - _extensions.push({ - name: name, - extension: extension - }); - _debug('Registered extension \'{}\'', name); - return true; - } - else - { - _info('Could not register extension with name \'{}\': another extension with the same name already exists'); - return false; - } - }; - - /** - * Unregister an extension previously registered with - * {@link #registerExtension(name, extension)}. - * @param name the name of the extension to unregister. - * @return true if the extension was unregistered, false otherwise - */ - this.unregisterExtension = function(name) - { - var unregistered = false; - $.each(_extensions, function(index, extension) - { - if (extension.name == name) - { - _extensions.splice(index, 1); - unregistered = true; - _debug('Unregistered extension \'{}\'', name); - return false; - } - }); - return unregistered; - }; - - /** - * Starts a the batch of messages to be sent in a single request. - * @see _endBatch(deliverMessages) - */ - function _startBatch() - { - ++_batch; - }; - - /** - * Ends the batch of messages to be sent in a single request, - * optionally delivering messages present in the message queue depending - * on the given argument. - * @param deliverMessages whether to deliver the messages in the queue or not - * @see _startBatch() - */ - function _endBatch(deliverMessages) - { - --_batch; - if (_batch < 0) _batch = 0; - if (deliverMessages && _batch == 0 && !_isDisconnected()) - { - var messages = _messageQueue; - _messageQueue = []; - if (messages.length > 0) _deliver(messages, false); - } - }; - - function _nextMessageId() - { - return ++_messageId; - }; - - /** - * Converts the given response into an array of bayeux messages - * @param response the response to convert - * @return an array of bayeux messages obtained by converting the response - */ - function _convertToMessages(response) - { - if (response === undefined) return []; - if (response instanceof Array) return response; - if (response instanceof String || typeof response == 'string') return eval('(' + response + ')'); - if (response instanceof Object) return [response]; - throw 'Conversion Error ' + response + ', typeof ' + (typeof response); - }; - - function _setStatus(newStatus) - { - _debug('{} -> {}', _status, newStatus); - _status = newStatus; - }; - - function _isDisconnected() - { - return _status == 'disconnecting' || _status == 'disconnected'; - }; - - /** - * Sends the initial handshake message - */ - function _handshake(handshakeProps) - { - _debug('Starting handshake'); - _clientId = null; - - // Start a batch. - // This is needed because handshake and connect are async. - // It may happen that the application calls init() then subscribe() - // and the subscribe message is sent before the connect message, if - // the subscribe message is not held until the connect message is sent. - // So here we start a batch to hold temporarly any message until - // the connection is fully established. - _batch = 0; - _startBatch(); - - // Save the original properties provided by the user - // Deep copy to avoid the user to be able to change them later - _handshakeProps = $.extend(true, {}, handshakeProps); - - var bayeuxMessage = { - version: '1.0', - minimumVersion: '0.9', - channel: '/meta/handshake', - supportedConnectionTypes: _xd ? ['callback-polling'] : ['long-polling', 'callback-polling'] - }; - // Do not allow the user to mess with the required properties, - // so merge first the user properties and *then* the bayeux message - var message = $.extend({}, handshakeProps, bayeuxMessage); - - // We started a batch to hold the application messages, - // so here we must bypass it and deliver immediately. - _setStatus('handshaking'); - _deliver([message], false); - }; - - function _findTransport(handshakeResponse) - { - var transportTypes = handshakeResponse.supportedConnectionTypes; - if (_xd) - { - // If we are cross domain, check if the server supports it, that's the only option - if ($.inArray('callback-polling', transportTypes) >= 0) return _transport; - } - else - { - // Check if we can keep long-polling - if ($.inArray('long-polling', transportTypes) >= 0) return _transport; - - // The server does not support long-polling - if ($.inArray('callback-polling', transportTypes) >= 0) return newCallbackPollingTransport(); - } - return null; - }; - - function _delayedHandshake() - { - _setStatus('handshaking'); - _delayedSend(function() - { - _handshake(_handshakeProps); - }); - }; - - function _delayedConnect() - { - _setStatus('connecting'); - _delayedSend(function() - { - _connect(); - }); - }; - - function _delayedSend(operation) - { - _cancelDelayedSend(); - var delay = _backoff; - _debug("Delayed send: backoff {}, interval {}", _backoff, _advice.interval); - if (_advice.interval && _advice.interval > 0) - delay += _advice.interval; - _scheduledSend = _setTimeout(operation, delay); - }; - - function _cancelDelayedSend() - { - if (_scheduledSend !== null) clearTimeout(_scheduledSend); - _scheduledSend = null; - }; - - function _setTimeout(funktion, delay) - { - return setTimeout(function() - { - try - { - funktion(); - } - catch (x) - { - _debug('Exception during scheduled execution of function \'{}\': {}', funktion.name, x); - } - }, delay); - }; - - /** - * Sends the connect message - */ - function _connect() - { - _debug('Starting connect'); - var message = { - channel: '/meta/connect', - connectionType: _transport.getType() - }; - _setStatus('connecting'); - _deliver([message], true); - _setStatus('connected'); - }; - - function _send(message) - { - if (_batch > 0) - _messageQueue.push(message); - else - _deliver([message], false); - }; - - /** - * Delivers the messages to the comet server - * @param messages the array of messages to send - */ - function _deliver(messages, comet) - { - // We must be sure that the messages have a clientId. - // This is not guaranteed since the handshake may take time to return - // (and hence the clientId is not known yet) and the application - // may create other messages. - $.each(messages, function(index, message) - { - message['id'] = _nextMessageId(); - if (_clientId) message['clientId'] = _clientId; - messages[index] = _applyOutgoingExtensions(message); - }); - - var self = this; - var envelope = { - url: _url, - messages: messages, - onSuccess: function(request, response) - { - try - { - _handleSuccess.call(self, request, response, comet); - } - catch (x) - { - _debug('Exception during execution of success callback: {}', x); - } - }, - onFailure: function(request, reason, exception) - { - try - { - _handleFailure.call(self, request, messages, reason, exception, comet); - } - catch (x) - { - _debug('Exception during execution of failure callback: {}', x); - } - } - }; - _debug('Sending request to {}, message(s): {}', envelope.url, JSON.stringify(envelope.messages)); - _transport.send(envelope, comet); - }; - - function _applyIncomingExtensions(message) - { - for (var i = 0; i < _extensions.length; ++i) - { - var extension = _extensions[i]; - var callback = extension.extension.incoming; - if (callback && typeof callback === 'function') - { - _debug('Calling incoming extension \'{}\', callback \'{}\'', extension.name, callback.name); - message = _applyExtension(extension.name, callback, message) || message; - } - } - return message; - }; - - function _applyOutgoingExtensions(message) - { - for (var i = 0; i < _extensions.length; ++i) - { - var extension = _extensions[i]; - var callback = extension.extension.outgoing; - if (callback && typeof callback === 'function') - { - _debug('Calling outgoing extension \'{}\', callback \'{}\'', extension.name, callback.name); - message = _applyExtension(extension.name, callback, message) || message; - } - } - return message; - }; - - function _applyExtension(name, callback, message) - { - try - { - return callback(message); - } - catch (x) - { - _debug('Exception during execution of extension \'{}\': {}', name, x); - return message; - } - }; - - function _handleSuccess(request, response, comet) - { - var messages = _convertToMessages(response); - _debug('Received response {}', JSON.stringify(messages)); - - // Signal the transport it can deliver other queued requests - _transport.complete(request, true, comet); - - for (var i = 0; i < messages.length; ++i) - { - var message = messages[i]; - message = _applyIncomingExtensions(message); - - if (message.advice) _advice = message.advice; - - var channel = message.channel; - switch (channel) - { - case '/meta/handshake': - _handshakeSuccess(message); - break; - case '/meta/connect': - _connectSuccess(message); - break; - case '/meta/disconnect': - _disconnectSuccess(message); - break; - case '/meta/subscribe': - _subscribeSuccess(message); - break; - case '/meta/unsubscribe': - _unsubscribeSuccess(message); - break; - default: - _messageSuccess(message); - break; - } - } - }; - - function _handleFailure(request, messages, reason, exception, comet) - { - var xhr = request.xhr; - _debug('Request failed, status: {}, reason: {}, exception: {}', xhr && xhr.status, reason, exception); - - // Signal the transport it can deliver other queued requests - _transport.complete(request, false, comet); - - for (var i = 0; i < messages.length; ++i) - { - var message = messages[i]; - var channel = message.channel; - switch (channel) - { - case '/meta/handshake': - _handshakeFailure(xhr, message); - break; - case '/meta/connect': - _connectFailure(xhr, message); - break; - case '/meta/disconnect': - _disconnectFailure(xhr, message); - break; - case '/meta/subscribe': - _subscribeFailure(xhr, message); - break; - case '/meta/unsubscribe': - _unsubscribeFailure(xhr, message); - break; - default: - _messageFailure(xhr, message); - break; - } - } - }; - - function _handshakeSuccess(message) - { - if (message.successful) - { - _debug('Handshake successful'); - // Save clientId, figure out transport, then follow the advice to connect - _clientId = message.clientId; - - var newTransport = _findTransport(message); - if (newTransport === null) - { - throw 'Could not agree on transport with server'; - } - else - { - if (_transport.getType() != newTransport.getType()) - { - _debug('Changing transport from {} to {}', _transport.getType(), newTransport.getType()); - _transport = newTransport; - } - } - - // Notify the listeners - // Here the new transport is in place, as well as the clientId, so - // the listener can perform a publish() if it wants, and the listeners - // are notified before the connect below. - _notifyListeners('/meta/handshake', message); - - var action = _advice.reconnect ? _advice.reconnect : 'retry'; - switch (action) - { - case 'retry': - _delayedConnect(); - break; - default: - break; - } - } - else - { - _debug('Handshake unsuccessful'); - - var retry = !_isDisconnected() && _advice.reconnect != 'none'; - if (!retry) _setStatus('disconnected'); - - _notifyListeners('/meta/handshake', message); - _notifyListeners('/meta/unsuccessful', message); - - // Only try again if we haven't been disconnected and - // the advice permits us to retry the handshake - if (retry) - { - _increaseBackoff(); - _debug('Handshake failure, backing off and retrying in {} ms', _backoff); - _delayedHandshake(); - } - } - }; - - function _handshakeFailure(xhr, message) - { - _debug('Handshake failure'); - - // Notify listeners - var failureMessage = { - successful: false, - failure: true, - channel: '/meta/handshake', - request: message, - xhr: xhr, - advice: { - action: 'retry', - interval: _backoff - } - }; - - var retry = !_isDisconnected() && _advice.reconnect != 'none'; - if (!retry) _setStatus('disconnected'); - - _notifyListeners('/meta/handshake', failureMessage); - _notifyListeners('/meta/unsuccessful', failureMessage); - - // Only try again if we haven't been disconnected and the - // advice permits us to try again - if (retry) - { - _increaseBackoff(); - _debug('Handshake failure, backing off and retrying in {} ms', _backoff); - _delayedHandshake(); - } - }; - - function _connectSuccess(message) - { - var action = _isDisconnected() ? 'none' : (_advice.reconnect ? _advice.reconnect : 'retry'); - if (!_isDisconnected()) _setStatus(action == 'retry' ? 'connecting' : 'disconnecting'); - - if (message.successful) - { - _debug('Connect successful'); - - // End the batch and allow held messages from the application - // to go to the server (see _handshake() where we start the batch). - // The batch is ended before notifying the listeners, so that - // listeners can batch other cometd operations - _endBatch(true); - - // Notify the listeners after the status change but before the next connect - _notifyListeners('/meta/connect', message); - - // Connect was successful. - // Normally, the advice will say "reconnect: 'retry', interval: 0" - // and the server will hold the request, so when a response returns - // we immediately call the server again (long polling) - switch (action) - { - case 'retry': - _resetBackoff(); - _delayedConnect(); - break; - default: - _resetBackoff(); - _setStatus('disconnected'); - break; - } - } - else - { - _debug('Connect unsuccessful'); - - // Notify the listeners after the status change but before the next action - _notifyListeners('/meta/connect', message); - _notifyListeners('/meta/unsuccessful', message); - - // Connect was not successful. - // This may happen when the server crashed, the current clientId - // will be invalid, and the server will ask to handshake again - switch (action) - { - case 'retry': - _increaseBackoff(); - _delayedConnect(); - break; - case 'handshake': - // End the batch but do not deliver the messages until we connect successfully - _endBatch(false); - _resetBackoff(); - _delayedHandshake(); - break; - case 'none': - _resetBackoff(); - _setStatus('disconnected'); - break; - } - } - }; - - function _connectFailure(xhr, message) - { - _debug('Connect failure'); - - // Notify listeners - var failureMessage = { - successful: false, - failure: true, - channel: '/meta/connect', - request: message, - xhr: xhr, - advice: { - action: 'retry', - interval: _backoff - } - }; - _notifyListeners('/meta/connect', failureMessage); - _notifyListeners('/meta/unsuccessful', failureMessage); - - if (!_isDisconnected()) - { - var action = _advice.reconnect ? _advice.reconnect : 'retry'; - switch (action) - { - case 'retry': - _increaseBackoff(); - _debug('Connect failure, backing off and retrying in {} ms', _backoff); - _delayedConnect(); - break; - case 'handshake': - _resetBackoff(); - _delayedHandshake(); - break; - case 'none': - _resetBackoff(); - break; - default: - _debug('Unrecognized reconnect value: {}', action); - break; - } - } - }; - - function _disconnectSuccess(message) - { - if (message.successful) - { - _debug('Disconnect successful'); - _disconnect(false); - _notifyListeners('/meta/disconnect', message); - } - else - { - _debug('Disconnect unsuccessful'); - _disconnect(true); - _notifyListeners('/meta/disconnect', message); - _notifyListeners('/meta/usuccessful', message); - } - }; - - function _disconnect(abort) - { - _cancelDelayedSend(); - if (abort) _transport.abort(); - _clientId = null; - _setStatus('disconnected'); - _batch = 0; - _messageQueue = []; - _resetBackoff(); - }; - - function _disconnectFailure(xhr, message) - { - _debug('Disconnect failure'); - _disconnect(true); - - var failureMessage = { - successful: false, - failure: true, - channel: '/meta/disconnect', - request: message, - xhr: xhr, - advice: { - action: 'none', - interval: 0 - } - }; - _notifyListeners('/meta/disconnect', failureMessage); - _notifyListeners('/meta/unsuccessful', failureMessage); - }; - - function _subscribeSuccess(message) - { - if (message.successful) - { - _debug('Subscribe successful'); - _notifyListeners('/meta/subscribe', message); - } - else - { - _debug('Subscribe unsuccessful'); - _notifyListeners('/meta/subscribe', message); - _notifyListeners('/meta/unsuccessful', message); - } - }; - - function _subscribeFailure(xhr, message) - { - _debug('Subscribe failure'); - - var failureMessage = { - successful: false, - failure: true, - channel: '/meta/subscribe', - request: message, - xhr: xhr, - advice: { - action: 'none', - interval: 0 - } - }; - _notifyListeners('/meta/subscribe', failureMessage); - _notifyListeners('/meta/unsuccessful', failureMessage); - }; - - function _unsubscribeSuccess(message) - { - if (message.successful) - { - _debug('Unsubscribe successful'); - _notifyListeners('/meta/unsubscribe', message); - } - else - { - _debug('Unsubscribe unsuccessful'); - _notifyListeners('/meta/unsubscribe', message); - _notifyListeners('/meta/unsuccessful', message); - } - }; - - function _unsubscribeFailure(xhr, message) - { - _debug('Unsubscribe failure'); - - var failureMessage = { - successful: false, - failure: true, - channel: '/meta/unsubscribe', - request: message, - xhr: xhr, - advice: { - action: 'none', - interval: 0 - } - }; - _notifyListeners('/meta/unsubscribe', failureMessage); - _notifyListeners('/meta/unsuccessful', failureMessage); - }; - - function _messageSuccess(message) - { - if (message.successful === undefined) - { - if (message.data) - { - // It is a plain message, and not a bayeux meta message - _notifyListeners(message.channel, message); - } - else - { - _debug('Unknown message {}', JSON.stringify(message)); - } - } - else - { - if (message.successful) - { - _debug('Publish successful'); - _notifyListeners('/meta/publish', message); - } - else - { - _debug('Publish unsuccessful'); - _notifyListeners('/meta/publish', message); - _notifyListeners('/meta/unsuccessful', message); - } - } - }; - - function _messageFailure(xhr, message) - { - _debug('Publish failure'); - - var failureMessage = { - successful: false, - failure: true, - channel: message.channel, - request: message, - xhr: xhr, - advice: { - action: 'none', - interval: 0 - } - }; - _notifyListeners('/meta/publish', failureMessage); - _notifyListeners('/meta/unsuccessful', failureMessage); - }; - - function _notifyListeners(channel, message) - { - // Notify direct listeners - _notify(channel, message); - - // Notify the globbing listeners - var channelParts = channel.split("/"); - var last = channelParts.length - 1; - for (var i = last; i > 0; --i) - { - var channelPart = channelParts.slice(0, i).join('/') + '/*'; - // We don't want to notify /foo/* if the channel is /foo/bar/baz, - // so we stop at the first non recursive globbing - if (i == last) _notify(channelPart, message); - // Add the recursive globber and notify - channelPart += '*'; - _notify(channelPart, message); - } - }; - - function _notify(channel, message) - { - var subscriptions = _listeners[channel]; - if (subscriptions && subscriptions.length > 0) - { - for (var i = 0; i < subscriptions.length; ++i) - { - var subscription = subscriptions[i]; - // Subscriptions may come and go, so the array may have 'holes' - if (subscription) - { - try - { - _debug('Notifying subscription: channel \'{}\', callback \'{}\'', channel, subscription.callback.name); - subscription.callback.call(subscription.scope, message); - } - catch (x) - { - // Ignore exceptions from callbacks - _warn('Exception during execution of callback \'{}\' on channel \'{}\' for message {}, exception: {}', subscription.callback.name, channel, JSON.stringify(message), x); - } - } - } - } - }; - - function _resetBackoff() - { - _backoff = 0; - }; - - function _increaseBackoff() - { - if (_backoff < _maxBackoff) _backoff += _backoffIncrement; - }; - - var _error = this._error = function(text, args) - { - _log('error', _format.apply(this, arguments)); - }; - - var _warn = this._warn = function(text, args) - { - _log('warn', _format.apply(this, arguments)); - }; - - var _info = this._info = function(text, args) - { - _log('info', _format.apply(this, arguments)); - }; - - var _debug = this._debug = function(text, args) - { - _log('debug', _format.apply(this, arguments)); - }; - - function _log(level, text) - { - var priority = _logPriorities[level]; - var configPriority = _logPriorities[_logLevel]; - if (!configPriority) configPriority = _logPriorities['info']; - if (priority >= configPriority) - { - if (window.console) window.console.log(text); - } - }; - - function _format(text) - { - var braces = /\{\}/g; - var result = ''; - var start = 0; - var count = 0; - while (braces.test(text)) - { - result += text.substr(start, braces.lastIndex - start - 2); - var arg = arguments[++count]; - result += arg !== undefined ? arg : '{}'; - start = braces.lastIndex; - } - result += text.substr(start, text.length - start); - return result; - }; - - function newLongPollingTransport() - { - return $.extend({}, new Transport('long-polling'), new LongPollingTransport()); - }; - - function newCallbackPollingTransport() - { - return $.extend({}, new Transport('callback-polling'), new CallbackPollingTransport()); - }; - - /** - * Base object with the common functionality for transports. - * The key responsibility is to allow at most 2 outstanding requests to the server, - * to avoid that requests are sent behind a long poll. - * To achieve this, we have one reserved request for the long poll, and all other - * requests are serialized one after the other. - */ - var Transport = function(type) - { - var _maxRequests = 2; - var _requestIds = 0; - var _cometRequest = null; - var _requests = []; - var _packets = []; - - this.getType = function() - { - return type; - }; - - this.send = function(packet, comet) - { - if (comet) - _cometSend(this, packet); - else - _send(this, packet); - }; - - function _cometSend(self, packet) - { - if (_cometRequest !== null) throw 'Concurrent comet requests not allowed, request ' + _cometRequest.id + ' not yet completed'; - - var requestId = ++_requestIds; - _debug('Beginning comet request {}', requestId); - - var request = {id: requestId}; - _debug('Delivering comet request {}', requestId); - self.deliver(packet, request); - _cometRequest = request; - }; - - function _send(self, packet) - { - var requestId = ++_requestIds; - _debug('Beginning request {}, {} other requests, {} queued requests', requestId, _requests.length, _packets.length); - - var request = {id: requestId}; - // Consider the comet request which should always be present - if (_requests.length < _maxRequests - 1) - { - _debug('Delivering request {}', requestId); - self.deliver(packet, request); - _requests.push(request); - } - else - { - _packets.push([packet, request]); - _debug('Queued request {}, {} queued requests', requestId, _packets.length); - } - }; - - this.complete = function(request, success, comet) - { - if (comet) - _cometComplete(request); - else - _complete(this, request, success); - }; - - function _cometComplete(request) - { - var requestId = request.id; - if (_cometRequest !== request) throw 'Comet request mismatch, completing request ' + requestId; - - // Reset comet request - _cometRequest = null; - _debug('Ended comet request {}', requestId); - }; - - function _complete(self, request, success) - { - var requestId = request.id; - var index = $.inArray(request, _requests); - // The index can be negative the request has been aborted - if (index >= 0) _requests.splice(index, 1); - _debug('Ended request {}, {} other requests, {} queued requests', requestId, _requests.length, _packets.length); - - if (_packets.length > 0) - { - var packet = _packets.shift(); - if (success) - { - _debug('Dequeueing and sending request {}, {} queued requests', packet[1].id, _packets.length); - _send(self, packet[0]); - } - else - { - _debug('Dequeueing and failing request {}, {} queued requests', packet[1].id, _packets.length); - // Keep the semantic of calling response callbacks asynchronously after the request - setTimeout(function() { packet[0].onFailure(packet[1], 'error'); }, 0); - } - } - }; - - this.abort = function() - { - for (var i = 0; i < _requests.length; ++i) - { - var request = _requests[i]; - _debug('Aborting request {}', request.id); - if (request.xhr) request.xhr.abort(); - } - if (_cometRequest) - { - _debug('Aborting comet request {}', _cometRequest.id); - if (_cometRequest.xhr) _cometRequest.xhr.abort(); - } - _cometRequest = null; - _requests = []; - _packets = []; - }; - }; - - var LongPollingTransport = function() - { - this.deliver = function(packet, request) - { - request.xhr = $.ajax({ - url: packet.url, - type: 'POST', - contentType: 'text/json;charset=UTF-8', - beforeSend: function(xhr) - { - xhr.setRequestHeader('Connection', 'Keep-Alive'); - return true; - }, - data: JSON.stringify(packet.messages), - success: function(response) { packet.onSuccess(request, response); }, - error: function(xhr, reason, exception) { packet.onFailure(request, reason, exception); } - }); - }; - }; - - var CallbackPollingTransport = function() - { - var _maxLength = 2000; - this.deliver = function(packet, request) - { - // Microsoft Internet Explorer has a 2083 URL max length - // We must ensure that we stay within that length - var messages = JSON.stringify(packet.messages); - // Encode the messages because all brackets, quotes, commas, colons, etc - // present in the JSON will be URL encoded, taking many more characters - var urlLength = packet.url.length + encodeURI(messages).length; - _debug('URL length: {}', urlLength); - // Let's stay on the safe side and use 2000 instead of 2083 - // also because we did not count few characters among which - // the parameter name 'message' and the parameter 'jsonp', - // which sum up to about 50 chars - if (urlLength > _maxLength) - { - var x = packet.messages.length > 1 ? - 'Too many bayeux messages in the same batch resulting in message too big ' + - '(' + urlLength + ' bytes, max is ' + _maxLength + ') for transport ' + this.getType() : - 'Bayeux message too big (' + urlLength + ' bytes, max is ' + _maxLength + ') ' + - 'for transport ' + this.getType(); - // Keep the semantic of calling response callbacks asynchronously after the request - _setTimeout(function() { packet.onFailure(request, 'error', x); }, 0); - } - else - { - $.ajax({ - url: packet.url, - type: 'GET', - dataType: 'jsonp', - jsonp: 'jsonp', - beforeSend: function(xhr) - { - xhr.setRequestHeader('Connection', 'Keep-Alive'); - return true; - }, - data: - { - // In callback-polling, the content must be sent via the 'message' parameter - message: messages - }, - success: function(response) { packet.onSuccess(request, response); }, - error: function(xhr, reason, exception) { packet.onFailure(request, reason, exception); } - }); - } - }; - }; - }; - - /** - * The JS object that exposes the comet API to applications - */ - $.cometd = new $.Cometd(); // The default instance - -})(jQuery); diff --git a/plugins/Orbited/json2.js b/plugins/Orbited/json2.js deleted file mode 100644 index 7e27df518..000000000 --- a/plugins/Orbited/json2.js +++ /dev/null @@ -1,478 +0,0 @@ -/* - http://www.JSON.org/json2.js - 2009-04-16 - - Public Domain. - - NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. - - See http://www.JSON.org/js.html - - This file creates a global JSON object containing two methods: stringify - and parse. - - JSON.stringify(value, replacer, space) - value any JavaScript value, usually an object or array. - - replacer an optional parameter that determines how object - values are stringified for objects. It can be a - function or an array of strings. - - space an optional parameter that specifies the indentation - of nested structures. If it is omitted, the text will - be packed without extra whitespace. If it is a number, - it will specify the number of spaces to indent at each - level. If it is a string (such as '\t' or ' '), - it contains the characters used to indent at each level. - - This method produces a JSON text from a JavaScript value. - - When an object value is found, if the object contains a toJSON - method, its toJSON method will be called and the result will be - stringified. A toJSON method does not serialize: it returns the - value represented by the name/value pair that should be serialized, - or undefined if nothing should be serialized. The toJSON method - will be passed the key associated with the value, and this will be - bound to the object holding the key. - - For example, this would serialize Dates as ISO strings. - - Date.prototype.toJSON = function (key) { - function f(n) { - // Format integers to have at least two digits. - return n < 10 ? '0' + n : n; - } - - return this.getUTCFullYear() + '-' + - f(this.getUTCMonth() + 1) + '-' + - f(this.getUTCDate()) + 'T' + - f(this.getUTCHours()) + ':' + - f(this.getUTCMinutes()) + ':' + - f(this.getUTCSeconds()) + 'Z'; - }; - - You can provide an optional replacer method. It will be passed the - key and value of each member, with this bound to the containing - object. The value that is returned from your method will be - serialized. If your method returns undefined, then the member will - be excluded from the serialization. - - If the replacer parameter is an array of strings, then it will be - used to select the members to be serialized. It filters the results - such that only members with keys listed in the replacer array are - stringified. - - Values that do not have JSON representations, such as undefined or - functions, will not be serialized. Such values in objects will be - dropped; in arrays they will be replaced with null. You can use - a replacer function to replace those with JSON values. - JSON.stringify(undefined) returns undefined. - - The optional space parameter produces a stringification of the - value that is filled with line breaks and indentation to make it - easier to read. - - If the space parameter is a non-empty string, then that string will - be used for indentation. If the space parameter is a number, then - the indentation will be that many spaces. - - Example: - - text = JSON.stringify(['e', {pluribus: 'unum'}]); - // text is '["e",{"pluribus":"unum"}]' - - - text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); - // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' - - text = JSON.stringify([new Date()], function (key, value) { - return this[key] instanceof Date ? - 'Date(' + this[key] + ')' : value; - }); - // text is '["Date(---current time---)"]' - - - JSON.parse(text, reviver) - This method parses a JSON text to produce an object or array. - It can throw a SyntaxError exception. - - The optional reviver parameter is a function that can filter and - transform the results. It receives each of the keys and values, - and its return value is used instead of the original value. - If it returns what it received, then the structure is not modified. - If it returns undefined then the member is deleted. - - Example: - - // Parse the text. Values that look like ISO date strings will - // be converted to Date objects. - - myData = JSON.parse(text, function (key, value) { - var a; - if (typeof value === 'string') { - a = -/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); - if (a) { - return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], - +a[5], +a[6])); - } - } - return value; - }); - - myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { - var d; - if (typeof value === 'string' && - value.slice(0, 5) === 'Date(' && - value.slice(-1) === ')') { - d = new Date(value.slice(5, -1)); - if (d) { - return d; - } - } - return value; - }); - - - This is a reference implementation. You are free to copy, modify, or - redistribute. - - This code should be minified before deployment. - See http://javascript.crockford.com/jsmin.html - - USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO - NOT CONTROL. -*/ - -/*jslint evil: true */ - -/*global JSON */ - -/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, - call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, - getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, - lastIndex, length, parse, prototype, push, replace, slice, stringify, - test, toJSON, toString, valueOf -*/ - -// Create a JSON object only if one does not already exist. We create the -// methods in a closure to avoid creating global variables. - -if (!this.JSON) { - JSON = {}; -} -(function () { - - function f(n) { - // Format integers to have at least two digits. - return n < 10 ? '0' + n : n; - } - - if (typeof Date.prototype.toJSON !== 'function') { - - Date.prototype.toJSON = function (key) { - - return this.getUTCFullYear() + '-' + - f(this.getUTCMonth() + 1) + '-' + - f(this.getUTCDate()) + 'T' + - f(this.getUTCHours()) + ':' + - f(this.getUTCMinutes()) + ':' + - f(this.getUTCSeconds()) + 'Z'; - }; - - String.prototype.toJSON = - Number.prototype.toJSON = - Boolean.prototype.toJSON = function (key) { - return this.valueOf(); - }; - } - - var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - gap, - indent, - meta = { // table of character substitutions - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '"' : '\\"', - '\\': '\\\\' - }, - rep; - - - function quote(string) { - -// If the string contains no control characters, no quote characters, and no -// backslash characters, then we can safely slap some quotes around it. -// Otherwise we must also replace the offending characters with safe escape -// sequences. - - escapable.lastIndex = 0; - return escapable.test(string) ? - '"' + string.replace(escapable, function (a) { - var c = meta[a]; - return typeof c === 'string' ? c : - '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }) + '"' : - '"' + string + '"'; - } - - - function str(key, holder) { - -// Produce a string from holder[key]. - - var i, // The loop counter. - k, // The member key. - v, // The member value. - length, - mind = gap, - partial, - value = holder[key]; - -// If the value has a toJSON method, call it to obtain a replacement value. - - if (value && typeof value === 'object' && - typeof value.toJSON === 'function') { - value = value.toJSON(key); - } - -// If we were called with a replacer function, then call the replacer to -// obtain a replacement value. - - if (typeof rep === 'function') { - value = rep.call(holder, key, value); - } - -// What happens next depends on the value's type. - - switch (typeof value) { - case 'string': - return quote(value); - - case 'number': - -// JSON numbers must be finite. Encode non-finite numbers as null. - - return isFinite(value) ? String(value) : 'null'; - - case 'boolean': - case 'null': - -// If the value is a boolean or null, convert it to a string. Note: -// typeof null does not produce 'null'. The case is included here in -// the remote chance that this gets fixed someday. - - return String(value); - -// If the type is 'object', we might be dealing with an object or an array or -// null. - - case 'object': - -// Due to a specification blunder in ECMAScript, typeof null is 'object', -// so watch out for that case. - - if (!value) { - return 'null'; - } - -// Make an array to hold the partial results of stringifying this object value. - - gap += indent; - partial = []; - -// Is the value an array? - - if (Object.prototype.toString.apply(value) === '[object Array]') { - -// The value is an array. Stringify every element. Use null as a placeholder -// for non-JSON values. - - length = value.length; - for (i = 0; i < length; i += 1) { - partial[i] = str(i, value) || 'null'; - } - -// Join all of the elements together, separated with commas, and wrap them in -// brackets. - - v = partial.length === 0 ? '[]' : - gap ? '[\n' + gap + - partial.join(',\n' + gap) + '\n' + - mind + ']' : - '[' + partial.join(',') + ']'; - gap = mind; - return v; - } - -// If the replacer is an array, use it to select the members to be stringified. - - if (rep && typeof rep === 'object') { - length = rep.length; - for (i = 0; i < length; i += 1) { - k = rep[i]; - if (typeof k === 'string') { - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } else { - -// Otherwise, iterate through all of the keys in the object. - - for (k in value) { - if (Object.hasOwnProperty.call(value, k)) { - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } - -// Join all of the member texts together, separated with commas, -// and wrap them in braces. - - v = partial.length === 0 ? '{}' : - gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + - mind + '}' : '{' + partial.join(',') + '}'; - gap = mind; - return v; - } - } - -// If the JSON object does not yet have a stringify method, give it one. - - if (typeof JSON.stringify !== 'function') { - JSON.stringify = function (value, replacer, space) { - -// The stringify method takes a value and an optional replacer, and an optional -// space parameter, and returns a JSON text. The replacer can be a function -// that can replace values, or an array of strings that will select the keys. -// A default replacer method can be provided. Use of the space parameter can -// produce text that is more easily readable. - - var i; - gap = ''; - indent = ''; - -// If the space parameter is a number, make an indent string containing that -// many spaces. - - if (typeof space === 'number') { - for (i = 0; i < space; i += 1) { - indent += ' '; - } - -// If the space parameter is a string, it will be used as the indent string. - - } else if (typeof space === 'string') { - indent = space; - } - -// If there is a replacer, it must be a function or an array. -// Otherwise, throw an error. - - rep = replacer; - if (replacer && typeof replacer !== 'function' && - (typeof replacer !== 'object' || - typeof replacer.length !== 'number')) { - throw new Error('JSON.stringify'); - } - -// Make a fake root object containing our value under the key of ''. -// Return the result of stringifying the value. - - return str('', {'': value}); - }; - } - - -// If the JSON object does not yet have a parse method, give it one. - - if (typeof JSON.parse !== 'function') { - JSON.parse = function (text, reviver) { - -// The parse method takes a text and an optional reviver function, and returns -// a JavaScript value if the text is a valid JSON text. - - var j; - - function walk(holder, key) { - -// The walk method is used to recursively walk the resulting structure so -// that modifications can be made. - - var k, v, value = holder[key]; - if (value && typeof value === 'object') { - for (k in value) { - if (Object.hasOwnProperty.call(value, k)) { - v = walk(value, k); - if (v !== undefined) { - value[k] = v; - } else { - delete value[k]; - } - } - } - } - return reviver.call(holder, key, value); - } - - -// Parsing happens in four stages. In the first stage, we replace certain -// Unicode characters with escape sequences. JavaScript handles many characters -// incorrectly, either silently deleting them, or treating them as line endings. - - cx.lastIndex = 0; - if (cx.test(text)) { - text = text.replace(cx, function (a) { - return '\\u' + - ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }); - } - -// In the second stage, we run the text against regular expressions that look -// for non-JSON patterns. We are especially concerned with '()' and 'new' -// because they can cause invocation, and '=' because it can cause mutation. -// But just to be safe, we want to reject all unexpected forms. - -// We split the second stage into 4 regexp operations in order to work around -// crippling inefficiencies in IE's and Safari's regexp engines. First we -// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we -// replace all simple value tokens with ']' characters. Third, we delete all -// open brackets that follow a colon or comma or that begin the text. Finally, -// we look to see that the remaining characters are only whitespace or ']' or -// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. - - if (/^[\],:{}\s]*$/. -test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'). -replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). -replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { - -// In the third stage we use the eval function to compile the text into a -// JavaScript structure. The '{' operator is subject to a syntactic ambiguity -// in JavaScript: it can begin a block or an object literal. We wrap the text -// in parens to eliminate the ambiguity. - - j = eval('(' + text + ')'); - -// In the optional fourth stage, we recursively walk the new structure, passing -// each name/value pair to a reviver function for possible transformation. - - return typeof reviver === 'function' ? - walk({'': j}, '') : j; - } - -// If the text is not JSON parseable, then a SyntaxError is thrown. - - throw new SyntaxError('JSON.parse'); - }; - } -}()); |