- "+
+ "
- From "+ + "
- "+source+" "+ // may have a link, I think + "
- "+
+ "
- To "+ + "
- "+ + "in reply to"+ + " "+ + "
From 51ac34e80c5a99008b1a945b2c00b6dbfdde1529 Mon Sep 17 00:00:00 2001
From: Evan Prodromou
, add event for scripts that need to be in
---
lib/action.php | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/lib/action.php b/lib/action.php
index 71ceffe20..1b2f73752 100644
--- a/lib/action.php
+++ b/lib/action.php
@@ -120,14 +120,16 @@ class Action extends HTMLOutputter // lawsuit
{
// XXX: attributes (profile?)
$this->elementStart('head');
- $this->showTitle();
- $this->showShortcutIcon();
- $this->showStylesheets();
- $this->showScripts();
- $this->showOpenSearch();
- $this->showFeeds();
- $this->showDescription();
- $this->extraHead();
+ if (Event::handle('StartShowHeadElements', array($this))) {
+ $this->showTitle();
+ $this->showShortcutIcon();
+ $this->showStylesheets();
+ $this->showOpenSearch();
+ $this->showFeeds();
+ $this->showDescription();
+ $this->extraHead();
+ Event::handle('EndShowHeadElements', array($this));
+ }
$this->elementEnd('head');
}
@@ -352,6 +354,7 @@ class Action extends HTMLOutputter // lawsuit
Event::handle('EndShowFooter', array($this));
}
$this->elementEnd('div');
+ $this->showScripts();
$this->elementEnd('body');
}
--
cgit v1.2.3-54-g00ecf
From 22b4a66de3441bcb54b748f98be5b53e3be4bece Mon Sep 17 00:00:00 2001
From: Evan Prodromou "+html+" "+html+"$.cometd
,
+ * 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:
+ *
+ * var url2 = ...;
+ * var cometd2 = new $.Cometd();
+ * cometd2.init(url2);
+ *
+ */
+ $.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
+ * {
+ * 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.
+ *
+ * @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
new file mode 100644
index 000000000..7e27df518
--- /dev/null
+++ b/plugins/Orbited/json2.js
@@ -0,0 +1,478 @@
+/*
+ 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');
+ };
+ }
+}());
diff --git a/plugins/Orbited/updatetimeline.js b/plugins/Orbited/updatetimeline.js
new file mode 100644
index 000000000..170949e9b
--- /dev/null
+++ b/plugins/Orbited/updatetimeline.js
@@ -0,0 +1,154 @@
+// update the local timeline from a Comet server
+//
+
+var updater = function()
+{
+ var _server;
+ var _timeline;
+ var _userid;
+ var _replyurl;
+ var _favorurl;
+ var _deleteurl;
+ var _cometd;
+
+ return {
+ init: function(server, timeline, userid, replyurl, favorurl, deleteurl)
+ {
+ _cometd = $.cometd; // Uses the default Comet object
+ _cometd.setLogLevel('debug');
+ _cometd.init(server);
+ _server = server;
+ _timeline = timeline;
+ _userid = userid;
+ _favorurl = favorurl;
+ _replyurl = replyurl;
+ _deleteurl = deleteurl;
+ _cometd.subscribe(timeline, receive);
+ $(window).unload(leave);
+ }
+ }
+
+ function leave()
+ {
+ _cometd.disconnect();
+ }
+
+ function receive(message)
+ {
+ id = message.data.id;
+
+ // Don't add it if it already exists
+
+ if ($("#notice-"+id).length > 0) {
+ return;
+ }
+
+ var noticeItem = makeNoticeItem(message.data);
+ $("#notices_primary .notices").prepend(noticeItem, true);
+ $("#notices_primary .notice:first").css({display:"none"});
+ $("#notices_primary .notice:first").fadeIn(1000);
+ NoticeHover();
+ NoticeReply();
+ }
+
+ function makeNoticeItem(data)
+ {
+ user = data['user'];
+ html = data['html'].replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
+ source = data['source'].replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
+
+ ni = ""+
+ "
";
+
+ if (data['in_reply_to_status_id']) {
+ ni = ni+" "+
+ "
";
+ }
+
+ ni = ni+""+
+ "
";
+ return rl;
+ }
+
+ function makeDeleteLink(id)
+ {
+ var dl, delurl;
+ delurl = _deleteurl.replace("0000000000", id);
+
+ dl = ""+
+ "
";
+
+ return dl;
+ }
+}();
+
--
cgit v1.2.3-54-g00ecf
From 0d6314052fed12fd626bfa5519853f1d6ecb6814 Mon Sep 17 00:00:00 2001
From: Evan Prodromou $.cometd
,
- * 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:
- *
- * var url2 = ...;
- * var cometd2 = new $.Cometd();
- * cometd2.init(url2);
- *
- */
- $.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
- * {
- * 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.
- *
- * @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');
- };
- }
-}());
--
cgit v1.2.3-54-g00ecf
From 9c2d0879e9064fe3b9cf9af2c7eabe869966bd92 Mon Sep 17 00:00:00 2001
From: Evan Prodromou "+
- "
";
-
- if (data['in_reply_to_status_id']) {
- ni = ni+" "+
- "
";
- }
-
- ni = ni+""+
- "
";
- return rl;
- }
-
- function makeDeleteLink(id)
- {
- var dl, delurl;
- delurl = _deleteurl.replace("0000000000", id);
-
- dl = ""+
- "
";
-
- return dl;
- }
-}();
-
--
cgit v1.2.3-54-g00ecf
From 27ff66c9de64a60cb3219f6d823cb290d4f18c01 Mon Sep 17 00:00:00 2001
From: Evan Prodromou