diff --git a/ajax/libs/pubnub/3.4.6/pubnub.js b/ajax/libs/pubnub/3.4.6/pubnub.js
new file mode 100644
index 000000000..673abe827
--- /dev/null
+++ b/ajax/libs/pubnub/3.4.6/pubnub.js
@@ -0,0 +1,1408 @@
+// Version: 3.4.6
+/* =-====================================================================-= */
+/* =-====================================================================-= */
+/* =-========================= JSON =============================-= */
+/* =-====================================================================-= */
+/* =-====================================================================-= */
+
+(window['JSON'] && window['JSON']['stringify']) || (function () {
+ window['JSON'] || (window['JSON'] = {});
+
+ function toJSON(key) {
+ try { return this.valueOf() }
+ catch(e) { return null }
+ }
+
+ 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) {
+ 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) {
+ var i, // The loop counter.
+ k, // The member key.
+ v, // The member value.
+ length,
+ partial,
+ mind = gap,
+ value = holder[key];
+
+ if (value && typeof value === 'object') {
+ value = toJSON.call( value, key );
+ }
+
+ if (typeof rep === 'function') {
+ value = rep.call(holder, key, value);
+ }
+
+ switch (typeof value) {
+ case 'string':
+ return quote(value);
+
+ case 'number':
+ return isFinite(value) ? String(value) : 'null';
+
+ case 'boolean':
+ case 'null':
+ return String(value);
+
+ case 'object':
+
+ if (!value) {
+ return 'null';
+ }
+
+ gap += indent;
+ partial = [];
+
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
+
+ length = value.length;
+ for (i = 0; i < length; i += 1) {
+ partial[i] = str(i, value) || 'null';
+ }
+
+ v = partial.length === 0 ? '[]' :
+ gap ? '[\n' + gap +
+ partial.join(',\n' + gap) + '\n' +
+ mind + ']' :
+ '[' + partial.join(',') + ']';
+ gap = mind;
+ return v;
+ }
+ 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 {
+ for (k in value) {
+ if (Object.hasOwnProperty.call(value, k)) {
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
+ }
+ }
+ }
+ }
+
+ v = partial.length === 0 ? '{}' :
+ gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
+ mind + '}' : '{' + partial.join(',') + '}';
+ gap = mind;
+ return v;
+ }
+ }
+
+ if (typeof JSON['stringify'] !== 'function') {
+ JSON['stringify'] = function (value, replacer, space) {
+ var i;
+ gap = '';
+ indent = '';
+
+ if (typeof space === 'number') {
+ for (i = 0; i < space; i += 1) {
+ indent += ' ';
+ }
+ } else if (typeof space === 'string') {
+ indent = space;
+ }
+ rep = replacer;
+ if (replacer && typeof replacer !== 'function' &&
+ (typeof replacer !== 'object' ||
+ typeof replacer.length !== 'number')) {
+ throw new Error('JSON.stringify');
+ }
+ return str('', {'': value});
+ };
+ }
+
+ if (typeof JSON['parse'] !== 'function') {
+ // JSON is parsed on the server for security.
+ JSON['parse'] = function (text) {return eval('('+text+')')};
+ }
+}());
+var NOW = 1
+, READY = false
+, READY_BUFFER = []
+, PRESENCE_SUFFIX = '-pnpres'
+, DEF_WINDOWING = 10 // MILLISECONDS.
+, DEF_TIMEOUT = 10000 // MILLISECONDS.
+, DEF_SUB_TIMEOUT = 310 // SECONDS.
+, DEF_KEEPALIVE = 60 // SECONDS.
+, SECOND = 1000 // A THOUSAND MILLISECONDS.
+, URLBIT = '/'
+, PARAMSBIT = '&'
+, REPL = /{([\w\-]+)}/g;
+
+/**
+ * UTILITIES
+ */
+function unique() { return'x'+ ++NOW+''+(+new Date) }
+function rnow() { return+new Date }
+
+/**
+ * NEXTORIGIN
+ * ==========
+ * var next_origin = nextorigin();
+ */
+var nextorigin = (function() {
+ var max = 20
+ , ori = Math.floor(Math.random() * max);
+ return function( origin, failover ) {
+ return origin.indexOf('pubsub.') > 0
+ && origin.replace(
+ 'pubsub', 'ps' + (
+ failover ? uuid().split('-')[0] :
+ (++ori < max? ori : ori=1)
+ ) ) || origin;
+ }
+})();
+
+
+/**
+ * Build Url
+ * =======
+ *
+ */
+function build_url( url_components, url_params ) {
+ var url = url_components.join(URLBIT)
+ , params = [];
+
+ if (!url_params) return url;
+
+ each( url_params, function( key, value ) {
+ params.push(key + "=" + encode(value));
+ } );
+
+ url += "?" + params.join(PARAMSBIT);
+
+ return url;
+}
+
+/**
+ * UPDATER
+ * =======
+ * var timestamp = unique();
+ */
+function updater( fun, rate ) {
+ var timeout
+ , last = 0
+ , runnit = function() {
+ if (last + rate > rnow()) {
+ clearTimeout(timeout);
+ timeout = setTimeout( runnit, rate );
+ }
+ else {
+ last = rnow();
+ fun();
+ }
+ };
+
+ return runnit;
+}
+
+/**
+ * GREP
+ * ====
+ * var list = grep( [1,2,3], function(item) { return item % 2 } )
+ */
+function grep( list, fun ) {
+ var fin = [];
+ each( list || [], function(l) { fun(l) && fin.push(l) } );
+ return fin
+}
+
+/**
+ * SUPPLANT
+ * ========
+ * var text = supplant( 'Hello {name}!', { name : 'John' } )
+ */
+function supplant( str, values ) {
+ return str.replace( REPL, function( _, match ) {
+ return values[match] || _
+ } );
+}
+
+/**
+ * timeout
+ * =======
+ * timeout( function(){}, 100 );
+ */
+function timeout( fun, wait ) {
+ return setTimeout( fun, wait );
+}
+
+/**
+ * uuid
+ * ====
+ * var my_uuid = uuid();
+ */
+function uuid(callback) {
+ var u = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g,
+ function(c) {
+ var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
+ return v.toString(16);
+ });
+ if (callback) callback(u);
+ return u;
+}
+
+/**
+ * EACH
+ * ====
+ * each( [1,2,3], function(item) { } )
+ */
+function each( o, f ) {
+ if ( !o || !f ) return;
+
+ if ( typeof o[0] != 'undefined' )
+ for ( var i = 0, l = o.length; i < l; )
+ f.call( o[i], o[i], i++ );
+ else
+ for ( var i in o )
+ o.hasOwnProperty &&
+ o.hasOwnProperty(i) &&
+ f.call( o[i], i, o[i] );
+}
+
+/**
+ * MAP
+ * ===
+ * var list = map( [1,2,3], function(item) { return item + 1 } )
+ */
+function map( list, fun ) {
+ var fin = [];
+ each( list || [], function( k, v ) { fin.push(fun( k, v )) } );
+ return fin;
+}
+
+/**
+ * ENCODE
+ * ======
+ * var encoded_path = encode('path');
+ */
+function encode(path) {
+ return map( (encodeURIComponent(path)).split(''), function(chr) {
+ return "-_.!~*'()".indexOf(chr) < 0 ? chr :
+ "%"+chr.charCodeAt(0).toString(16).toUpperCase()
+ } ).join('');
+}
+
+/**
+ * Generate Subscription Channel List
+ * ==================================
+ * generate_channel_list(channels_object);
+ */
+function generate_channel_list(channels) {
+ var list = [];
+ each( channels, function( channel, status ) {
+ if (status.subscribed) list.push(channel);
+ } );
+ return list.sort();
+}
+
+// PUBNUB READY TO CONNECT
+function ready() { timeout( function() {
+ if (READY) return;
+ READY = 1;
+ each( READY_BUFFER, function(connect) { connect() } );
+}, SECOND ); }
+
+function PN_API(setup) {
+ var SUB_WINDOWING = +setup['windowing'] || DEF_WINDOWING
+ , SUB_TIMEOUT = (+setup['timeout'] || DEF_SUB_TIMEOUT) * SECOND
+ , KEEPALIVE = (+setup['keepalive'] || DEF_KEEPALIVE) * SECOND
+ , PUBLISH_KEY = setup['publish_key'] || ''
+ , SUBSCRIBE_KEY = setup['subscribe_key'] || ''
+ , SSL = setup['ssl'] ? 's' : ''
+ , ORIGIN = 'http'+SSL+'://'+(setup['origin']||'pubsub.pubnub.com')
+ , STD_ORIGIN = nextorigin(ORIGIN)
+ , SUB_ORIGIN = nextorigin(ORIGIN)
+ , CONNECT = function(){}
+ , PUB_QUEUE = []
+ , SUB_CALLBACK = 0
+ , SUB_CHANNEL = 0
+ , SUB_RECEIVER = []
+ , SUB_RESTORE = 0
+ , SUB_BUFF_WAIT = 0
+ , TIMETOKEN = 0
+ , CHANNELS = {}
+ , xdr = setup['xdr']
+ , error = setup['error'] || function() {}
+ , _is_online = setup['_is_online'] || function() { return 1; }
+ , jsonp_cb = setup['jsonp_cb'] || function(){ return 0; }
+ , db = setup['db'] || {'get': function(){}, 'set': function(){}}
+ , UUID = setup['uuid'] || ( db && db['get'](SUBSCRIBE_KEY+'uuid') || '');
+
+ function publish(next) {
+ if (next) PUB_QUEUE.sending = 0;
+ if (PUB_QUEUE.sending || !PUB_QUEUE.length) return;
+ PUB_QUEUE.sending = 1;
+ xdr(PUB_QUEUE.shift());
+ }
+
+ function each_channel(callback) {
+ each( generate_channel_list(CHANNELS), function(channel) {
+ var chan = CHANNELS[channel];
+ if (!chan) return;
+ callback(chan);
+ } );
+ }
+
+ // Announce Leave Event
+ var SELF = {
+ 'LEAVE' : function( channel, blocking ) {
+ var data = { 'uuid' : UUID}
+ , origin = nextorigin(ORIGIN)
+ , jsonp = jsonp_cb();
+
+ // Prevent Leaving a Presence Channel
+ if (channel.indexOf(PRESENCE_SUFFIX) > 0) return;
+
+ if (jsonp != '0') data['callback'] = jsonp;
+
+
+ xdr({
+ blocking : blocking || SSL,
+ timeout : 2000,
+ callback : jsonp,
+ data : data,
+ url : [
+ origin, 'v2', 'presence', 'sub_key',
+ SUBSCRIBE_KEY, 'channel', encode(channel), 'leave'
+ ]
+ });
+ },
+ /*
+ PUBNUB.history({
+ channel : 'my_chat_channel',
+ limit : 100,
+ callback : function(history) { }
+ });
+ */
+ 'history' : function( args, callback ) {
+ var callback = args['callback'] || callback
+ , count = args['count'] || args['limit'] || 100
+ , reverse = args['reverse'] || "false"
+ , err = args['error'] || function(){}
+ , channel = args['channel']
+ , start = args['start']
+ , end = args['end']
+ , params = {}
+ , jsonp = jsonp_cb();
+
+ // Make sure we have a Channel
+ if (!channel) return error('Missing Channel');
+ if (!callback) return error('Missing Callback');
+ if (!SUBSCRIBE_KEY) return error('Missing Subscribe Key');
+
+ params['stringtoken'] = 'true';
+ params['count'] = count;
+ params['reverse'] = reverse;
+
+ if (jsonp) params['callback'] = jsonp;
+ if (start) params['start'] = start;
+ if (end) params['end'] = end;
+
+ // Send Message
+ xdr({
+ callback : jsonp,
+ data : params,
+ success : function(response) { callback(response) },
+ fail : err,
+ url : [
+ STD_ORIGIN, 'v2', 'history', 'sub-key',
+ SUBSCRIBE_KEY, 'channel', encode(channel)
+ ]
+ });
+ },
+
+ /*
+ PUBNUB.replay({
+ source : 'my_channel',
+ destination : 'new_channel'
+ });
+ */
+ 'replay' : function(args) {
+ var callback = callback || args['callback'] || function(){}
+ , source = args['source']
+ , destination = args['destination']
+ , stop = args['stop']
+ , start = args['start']
+ , end = args['end']
+ , reverse = args['reverse']
+ , limit = args['limit']
+ , jsonp = jsonp_cb()
+ , data = {}
+ , url;
+
+ // Check User Input
+ if (!source) return error('Missing Source Channel');
+ if (!destination) return error('Missing Destination Channel');
+ if (!PUBLISH_KEY) return error('Missing Publish Key');
+ if (!SUBSCRIBE_KEY) return error('Missing Subscribe Key');
+
+ // Setup URL Params
+ if (jsonp != '0') data['callback'] = jsonp;
+ if (stop) data['stop'] = 'all';
+ if (reverse) data['reverse'] = 'true';
+ if (start) data['start'] = start;
+ if (end) data['end'] = end;
+ if (limit) data['count'] = limit;
+
+ // Compose URL Parts
+ url = [
+ STD_ORIGIN, 'v1', 'replay',
+ PUBLISH_KEY, SUBSCRIBE_KEY,
+ source, destination
+ ];
+
+ // Start (or Stop) Replay!
+ xdr({
+ callback : jsonp,
+ success : function(response) { callback(response) },
+ fail : function() { callback([ 0, 'Disconnected' ]) },
+ url : url,
+ data : data
+ });
+ },
+
+ /*
+ PUBNUB.time(function(time){ });
+ */
+ 'time' : function(callback) {
+ var jsonp = jsonp_cb();
+ xdr({
+ callback : jsonp,
+ timeout : SECOND*5,
+ url : [STD_ORIGIN, 'time', jsonp],
+ success : function(response) { callback(response[0]) },
+ fail : function() { callback(0) }
+ });
+ },
+
+ /*
+ PUBNUB.publish({
+ channel : 'my_chat_channel',
+ message : 'hello!'
+ });
+ */
+ 'publish' : function( args, callback ) {
+ var callback = callback || args['callback'] || function(){}
+ , msg = args['message']
+ , channel = args['channel']
+ , jsonp = jsonp_cb()
+ , url;
+
+ if (!msg) return error('Missing Message');
+ if (!channel) return error('Missing Channel');
+ if (!PUBLISH_KEY) return error('Missing Publish Key');
+ if (!SUBSCRIBE_KEY) return error('Missing Subscribe Key');
+
+ // If trying to send Object
+ msg = JSON['stringify'](msg);
+
+ // Create URL
+ url = [
+ STD_ORIGIN, 'publish',
+ PUBLISH_KEY, SUBSCRIBE_KEY,
+ 0, encode(channel),
+ jsonp, encode(msg)
+ ];
+
+ // Queue Message Send
+ PUB_QUEUE.push({
+ callback : jsonp,
+ timeout : SECOND*5,
+ url : url,
+ data : { 'uuid' : UUID },
+ success : function(response){callback(response);publish(1)},
+ fail : function(){callback([0,'Failed',msg]);publish(1)}
+ });
+
+ // Send Message
+ publish();
+ },
+
+ /*
+ PUBNUB.unsubscribe({ channel : 'my_chat' });
+ */
+ 'unsubscribe' : function(args) {
+ var channel = args['channel'];
+
+ TIMETOKEN = 0;
+ SUB_RESTORE = 1;
+
+ // Prepare Channel(s)
+ channel = map( (
+ channel.join ? channel.join(',') : ''+channel
+ ).split(','), function(channel) {
+ return channel + ',' + channel + PRESENCE_SUFFIX;
+ } ).join(',');
+
+ // Iterate over Channels
+ each( channel.split(','), function(channel) {
+ if (READY) SELF['LEAVE']( channel, 0 );
+ CHANNELS[channel] = 0;
+ } );
+
+ // ReOpen Connection if Any Channels Left
+ if (READY) CONNECT();
+ },
+
+ /*
+ PUBNUB.subscribe({
+ channel : 'my_chat'
+ callback : function(message) { }
+ });
+ */
+ 'subscribe' : function( args, callback ) {
+ var channel = args['channel']
+ , callback = callback || args['callback']
+ , callback = callback || args['message']
+ , connect = args['connect'] || function(){}
+ , reconnect = args['reconnect'] || function(){}
+ , disconnect = args['disconnect'] || function(){}
+ , presence = args['presence'] || 0
+ , noheresync = args['noheresync'] || 0
+ , backfill = args['backfill'] || 0
+ , sub_timeout = args['timeout'] || SUB_TIMEOUT
+ , windowing = args['windowing'] || SUB_WINDOWING
+ , restore = args['restore'];
+
+ // Restore Enabled?
+ SUB_RESTORE = restore;
+
+ // Always Reset the TT
+ TIMETOKEN = 0;
+
+ // Make sure we have a Channel
+ if (!channel) return error('Missing Channel');
+ if (!callback) return error('Missing Callback');
+ if (!SUBSCRIBE_KEY) return error('Missing Subscribe Key');
+
+ // Setup Channel(s)
+ each( (channel.join ? channel.join(',') : ''+channel).split(','),
+ function(channel) {
+ var settings = CHANNELS[channel] || {};
+
+ // Store Channel State
+ CHANNELS[SUB_CHANNEL = channel] = {
+ name : channel,
+ connected : settings.connected,
+ disconnected : settings.disconnected,
+ subscribed : 1,
+ callback : SUB_CALLBACK = callback,
+ connect : connect,
+ disconnect : disconnect,
+ reconnect : reconnect
+ };
+
+ // Presence Enabled?
+ if (!presence) return;
+
+ // Subscribe Presence Channel
+ SELF['subscribe']({
+ 'channel' : channel + PRESENCE_SUFFIX,
+ 'callback' : presence
+ });
+
+ // Presence Subscribed?
+ if (settings.subscribed) return;
+
+ // See Who's Here Now?
+ if (noheresync) return;
+ SELF['here_now']({
+ 'channel' : channel,
+ 'callback' : function(here) {
+ each( 'uuids' in here ? here['uuids'] : [],
+ function(uid) { presence( {
+ 'action' : 'join',
+ 'uuid' : uid,
+ 'timestamp' : rnow(),
+ 'occupancy' : here['occupancy'] || 1
+ }, here, channel ); } );
+ }
+ });
+ } );
+
+ // Test Network Connection
+ function _test_connection(success) {
+ if (success) {
+ // Begin Next Socket Connection
+ timeout( CONNECT, SECOND );
+ }
+ else {
+ // New Origin on Failed Connection
+ STD_ORIGIN = nextorigin( ORIGIN, 1 );
+ SUB_ORIGIN = nextorigin( ORIGIN, 1 );
+
+ // Re-test Connection
+ timeout( function() {
+ SELF['time'](_test_connection);
+ }, SECOND );
+ }
+
+ // Disconnect & Reconnect
+ each_channel(function(channel){
+ // Reconnect
+ if (success && channel.disconnected) {
+ channel.disconnected = 0;
+ return channel.reconnect(channel.name);
+ }
+
+ // Disconnect
+ if (!success && !channel.disconnected) {
+ channel.disconnected = 1;
+ channel.disconnect(channel.name);
+ }
+ });
+ }
+
+ // Evented Subscribe
+ function _connect() {
+ var jsonp = jsonp_cb()
+ , channels = generate_channel_list(CHANNELS).join(',');
+
+ // Stop Connection
+ if (!channels) return;
+
+ // Connect to PubNub Subscribe Servers
+ SUB_RECEIVER.push( xdr({
+ timeout : sub_timeout,
+ callback : jsonp,
+ fail : function() { SELF['time'](_test_connection) },
+ data : { 'uuid' : UUID },
+ url : [
+ SUB_ORIGIN, 'subscribe',
+ SUBSCRIBE_KEY, encode(channels),
+ jsonp, TIMETOKEN
+ ],
+ success : function(messages) {
+ if (!messages) return timeout( CONNECT, windowing );
+
+ // Connect
+ each_channel(function(channel){
+ if (channel.connected) return;
+ channel.connected = 1;
+ channel.connect(channel.name);
+ });
+
+ // Restore Previous Connection Point if Needed
+ TIMETOKEN = !TIMETOKEN &&
+ SUB_RESTORE &&
+ db['get'](SUBSCRIBE_KEY) || messages[1];
+
+ // Invoke Memory Catchup and Receive Up to 100
+ // Previous Messages from the Queue.
+ if (backfill) {
+ TIMETOKEN = 10000;
+ backfill = 0;
+ }
+
+ // Update Saved Timetoken
+ db['set']( SUBSCRIBE_KEY, messages[1] );
+
+ // Route Channel <---> Callback for Message
+ var next_callback = (function() {
+ var channels = (messages.length>2?messages[2]:'')
+ , list = channels.split(',');
+
+ return function() {
+ var channel = list.shift()||'';
+ return [
+ (CHANNELS[channel]||{})
+ .callback||SUB_CALLBACK,
+ (channel||SUB_CHANNEL)
+ .split(PRESENCE_SUFFIX)[0]
+ ];
+ };
+ })();
+
+ each( messages[0], function(msg) {
+ var next = next_callback();
+ if (!CHANNELS[next[1]].subscribed) return;
+ next[0]( msg, messages, next[1] );
+ } );
+
+ timeout( CONNECT, windowing );
+ }
+ }));
+ }
+ function CLOSE_PREVIOUS_SUB() {
+ while (SUB_RECEIVER.length) {
+ (SUB_RECEIVER.shift())();
+ }
+ }
+
+ CONNECT = function() {
+ CLOSE_PREVIOUS_SUB();
+ _connect();
+ };
+
+
+ // Reduce Status Flicker
+ if (!READY) return READY_BUFFER.push(CONNECT);
+
+ // Connect Now
+ CONNECT();
+ },
+
+ 'here_now' : function( args, callback ) {
+ var callback = args['callback'] || callback
+ , err = args['error'] || function(){}
+ , channel = args['channel']
+ , jsonp = jsonp_cb()
+ , data = null;
+
+ // Make sure we have a Channel
+ if (!channel) return error('Missing Channel');
+ if (!callback) return error('Missing Callback');
+ if (!SUBSCRIBE_KEY) return error('Missing Subscribe Key');
+
+ if (jsonp != '0') {
+ data = {};
+ data['callback'] = jsonp;
+ }
+
+ xdr({
+ callback : jsonp,
+ data : data,
+ success : function(response) { callback(response) },
+ fail : err,
+ url : [
+ STD_ORIGIN, 'v2', 'presence',
+ 'sub_key', SUBSCRIBE_KEY,
+ 'channel', encode(channel)
+ ]
+ });
+ },
+
+ // Expose PUBNUB Functions
+ 'xdr' : xdr,
+ 'ready' : ready,
+ 'db' : db,
+ 'uuid' : uuid,
+ 'map' : map,
+ 'each' : each,
+ 'each-channel' : each_channel,
+ 'grep' : grep,
+ 'supplant' : supplant,
+ 'now' : rnow,
+ 'unique' : unique,
+ 'updater' : updater
+ };
+
+ function _poll_online() {
+ _is_online() || _reset_offline();
+ timeout( _poll_online, SECOND );
+ }
+
+ function _poll_online2() {
+ SELF['time'](function(success){
+ success || _reset_offline();
+ timeout( _poll_online2, KEEPALIVE );
+ })
+ }
+
+ function _reset_offline() {
+ while (SUB_RECEIVER.length) {
+ (SUB_RECEIVER.shift())();
+ }
+ }
+
+ if (!UUID) UUID = SELF['uuid']();
+ db['set']( SUBSCRIBE_KEY + 'uuid', UUID );
+
+ timeout( _poll_online, SECOND );
+ timeout( _poll_online2, KEEPALIVE );
+
+ SELF['time'](function() {});
+
+ return SELF;
+}
+/* =-====================================================================-= */
+/* =-====================================================================-= */
+/* =-========================= UTIL =============================-= */
+/* =-====================================================================-= */
+/* =-====================================================================-= */
+
+window['PUBNUB'] || (function() {
+
+/**
+ * UTIL LOCALS
+ */
+
+var SWF = 'https://pubnub.a.ssl.fastly.net/pubnub.swf'
+, ASYNC = 'async'
+, UA = navigator.userAgent
+, PNSDK = 'PubNub-JS-' + 'Web' + '/' + '3.4.6'
+, XORIGN = UA.indexOf('MSIE 6') == -1;
+
+/**
+ * CONSOLE COMPATIBILITY
+ */
+window.console || (window.console=window.console||{});
+console.log || (
+ console.log =
+ console.error =
+ ((window.opera||{}).postError||function(){})
+);
+
+
+
+/**
+ * LOCAL STORAGE OR COOKIE
+ */
+var db = (function(){
+ var ls = window['localStorage'];
+ return {
+ 'get' : function(key) {
+ try {
+ if (ls) return ls.getItem(key);
+ if (document.cookie.indexOf(key) == -1) return null;
+ return ((document.cookie||'').match(
+ RegExp(key+'=([^;]+)')
+ )||[])[1] || null;
+ } catch(e) { return }
+ },
+ 'set' : function( key, value ) {
+ try {
+ if (ls) return ls.setItem( key, value ) && 0;
+ document.cookie = key + '=' + value +
+ '; expires=Thu, 1 Aug 2030 20:00:00 UTC; path=/';
+ } catch(e) { return }
+ }
+ };
+})();
+
+
+/**
+ * $
+ * =
+ * var div = $('divid');
+ */
+function $(id) { return document.getElementById(id) }
+
+/**
+ * ERROR
+ * =====
+ * error('message');
+ */
+function error(message) { console['error'](message) }
+
+/**
+ * SEARCH
+ * ======
+ * var elements = search('a div span');
+ */
+function search( elements, start ) {
+ var list = [];
+ each( elements.split(/\s+/), function(el) {
+ each( (start || document).getElementsByTagName(el), function(node) {
+ list.push(node);
+ } );
+ } );
+ return list;
+}
+
+
+
+/**
+ * BIND
+ * ====
+ * bind( 'keydown', search('a')[0], function(element) {
+ * ...
+ * } );
+ */
+function bind( type, el, fun ) {
+ each( type.split(','), function(etype) {
+ var rapfun = function(e) {
+ if (!e) e = window.event;
+ if (!fun(e)) {
+ e.cancelBubble = true;
+ e.returnValue = false;
+ e.preventDefault && e.preventDefault();
+ e.stopPropagation && e.stopPropagation();
+ }
+ };
+
+ if ( el.addEventListener ) el.addEventListener( etype, rapfun, false );
+ else if ( el.attachEvent ) el.attachEvent( 'on' + etype, rapfun );
+ else el[ 'on' + etype ] = rapfun;
+ } );
+}
+
+/**
+ * UNBIND
+ * ======
+ * unbind( 'keydown', search('a')[0] );
+ */
+function unbind( type, el, fun ) {
+ if ( el.removeEventListener ) el.removeEventListener( type, false );
+ else if ( el.detachEvent ) el.detachEvent( 'on' + type, false );
+ else el[ 'on' + type ] = null;
+}
+
+/**
+ * HEAD
+ * ====
+ * head().appendChild(elm);
+ */
+function head() { return search('head')[0] }
+
+/**
+ * ATTR
+ * ====
+ * var attribute = attr( node, 'attribute' );
+ */
+function attr( node, attribute, value ) {
+ if (value) node.setAttribute( attribute, value );
+ else return node && node.getAttribute && node.getAttribute(attribute);
+}
+
+/**
+ * CSS
+ * ===
+ * var obj = create('div');
+ */
+function css( element, styles ) {
+ for (var style in styles) if (styles.hasOwnProperty(style))
+ try {element.style[style] = styles[style] + (
+ '|width|height|top|left|'.indexOf(style) > 0 &&
+ typeof styles[style] == 'number'
+ ? 'px' : ''
+ )}catch(e){}
+}
+
+/**
+ * CREATE
+ * ======
+ * var obj = create('div');
+ */
+function create(element) { return document.createElement(element) }
+
+
+/**
+ * jsonp_cb
+ * ========
+ * var callback = jsonp_cb();
+ */
+function jsonp_cb() { return XORIGN || FDomainRequest() ? 0 : unique() }
+
+
+
+/**
+ * EVENTS
+ * ======
+ * PUBNUB.events.bind( 'you-stepped-on-flower', function(message) {
+ * // Do Stuff with message
+ * } );
+ *
+ * PUBNUB.events.fire( 'you-stepped-on-flower', "message-data" );
+ * PUBNUB.events.fire( 'you-stepped-on-flower', {message:"data"} );
+ * PUBNUB.events.fire( 'you-stepped-on-flower', [1,2,3] );
+ *
+ */
+var events = {
+ 'list' : {},
+ 'unbind' : function( name ) { events.list[name] = [] },
+ 'bind' : function( name, fun ) {
+ (events.list[name] = events.list[name] || []).push(fun);
+ },
+ 'fire' : function( name, data ) {
+ each(
+ events.list[name] || [],
+ function(fun) { fun(data) }
+ );
+ }
+};
+
+/**
+ * XDR Cross Domain Request
+ * ========================
+ * xdr({
+ * url : ['http://www.blah.com/url'],
+ * success : function(response) {},
+ * fail : function() {}
+ * });
+ */
+function xdr( setup ) {
+ if (XORIGN || FDomainRequest()) return ajax(setup);
+
+ var script = create('script')
+ , callback = setup.callback
+ , id = unique()
+ , finished = 0
+ , xhrtme = setup.timeout || DEF_TIMEOUT
+ , timer = timeout( function(){done(1)}, xhrtme )
+ , fail = setup.fail || function(){}
+ , data = setup.data || {}
+ , success = setup.success || function(){}
+
+ , append = function() {
+ head().appendChild(script);
+ }
+
+ , done = function( failed, response ) {
+ if (finished) return;
+ finished = 1;
+
+ failed || success(response);
+ script.onerror = null;
+ clearTimeout(timer);
+
+ timeout( function() {
+ failed && fail();
+ var s = $(id)
+ , p = s && s.parentNode;
+ p && p.removeChild(s);
+ }, SECOND );
+ };
+
+ window[callback] = function(response) {
+ done( 0, response );
+ };
+
+ if (!setup.blocking) script[ASYNC] = ASYNC;
+
+ script.onerror = function() { done(1) };
+ data['pnsdk'] = PNSDK;
+ script.src = build_url(setup.url,data);
+
+ attr( script, 'id', id );
+
+ append();
+ return done;
+}
+
+/**
+ * CORS XHR Request
+ * ================
+ * xdr({
+ * url : ['http://www.blah.com/url'],
+ * success : function(response) {},
+ * fail : function() {}
+ * });
+ */
+function ajax( setup ) {
+ var xhr, response
+ , finished = function() {
+ if (loaded) return;
+ loaded = 1;
+
+ clearTimeout(timer);
+
+ try { response = JSON['parse'](xhr.responseText); }
+ catch (r) { return done(1); }
+
+ success(response);
+ }
+ , complete = 0
+ , loaded = 0
+ , xhrtme = setup.timeout || DEF_TIMEOUT
+ , timer = timeout( function(){done(1)}, xhrtme )
+ , fail = setup.fail || function(){}
+ , data = setup.data || {}
+ , success = setup.success || function(){}
+ , async = ( typeof(setup.blocking) === 'undefined' )
+ , done = function(failed) {
+ if (complete) return;
+ complete = 1;
+
+ clearTimeout(timer);
+
+ if (xhr) {
+ xhr.onerror = xhr.onload = null;
+ xhr.abort && xhr.abort();
+ xhr = null;
+ }
+
+ failed && fail();
+ };
+
+ // Send
+ try {
+ xhr = FDomainRequest() ||
+ window.XDomainRequest &&
+ new XDomainRequest() ||
+ new XMLHttpRequest();
+
+ xhr.onerror = xhr.onabort = function(){ done(1) };
+ xhr.onload = xhr.onloadend = finished;
+ if (async) xhr.timeout = xhrtme;
+
+ data['pnsdk'] = PNSDK;
+ var url = build_url(setup.url,data);
+
+ xhr.open( 'GET', url, async );
+ xhr.send();
+ }
+ catch(eee) {
+ done(0);
+ XORIGN = 0;
+ return xdr(setup);
+ }
+
+ // Return 'done'
+ return done;
+}
+
+
+
+ // Test Connection State
+function _is_online() {
+ if (!('onLine' in navigator)) return 1;
+ return navigator['onLine'];
+}
+
+
+/* =-====================================================================-= */
+/* =-====================================================================-= */
+/* =-========================= PUBNUB ===========================-= */
+/* =-====================================================================-= */
+/* =-====================================================================-= */
+
+var PDIV = $('pubnub') || 0
+, CREATE_PUBNUB = function(setup) {
+
+ // Force JSONP if requested from user.
+ if (setup['jsonp']) XORIGN = 0;
+
+ var SUBSCRIBE_KEY = setup['subscribe_key'] || ''
+ , KEEPALIVE = (+setup['keepalive'] || DEF_KEEPALIVE) * SECOND
+ , UUID = setup['uuid'] || db['get'](SUBSCRIBE_KEY+'uuid')||'';
+
+ setup['xdr'] = xdr;
+ setup['db'] = db;
+ setup['error'] = error;
+ setup['_is_online'] = _is_online;
+ setup['jsonp_cb'] = jsonp_cb;
+
+ var SELF = PN_API(setup);
+ SELF['css'] = css;
+ SELF['$'] = $;
+ SELF['create'] = create;
+ SELF['bind'] = bind;
+ SELF['head'] = head;
+ SELF['search'] = search;
+ SELF['attr'] = attr;
+ SELF['events'] = events;
+ SELF['init'] = CREATE_PUBNUB;
+
+
+ // Add Leave Functions
+ bind( 'beforeunload', window, function() {
+ SELF['each-channel'](function(ch){ SELF['LEAVE']( ch.name, 1 ) });
+ return true;
+ } );
+
+ // Return without Testing
+ if (setup['notest']) return SELF;
+
+ bind( 'offline', window, SELF['_reset_offline'] );
+ bind( 'offline', document, SELF['_reset_offline'] );
+
+ // Return PUBNUB Socket Object
+ return SELF;
+};
+
+// Bind for PUBNUB Readiness to Subscribe
+bind( 'load', window, function(){ timeout( ready, 0 ) } );
+
+var pdiv = PDIV || {};
+
+// CREATE A PUBNUB GLOBAL OBJECT
+PUBNUB = CREATE_PUBNUB({
+ 'notest' : 1,
+ 'publish_key' : attr( pdiv, 'pub-key' ),
+ 'subscribe_key' : attr( pdiv, 'sub-key' ),
+ 'ssl' : !document.location.href.indexOf('https') ||
+ attr( pdiv, 'ssl' ) == 'on',
+ 'origin' : attr( pdiv, 'origin' ),
+ 'uuid' : attr( pdiv, 'uuid' )
+});
+
+// jQuery Interface
+window['jQuery'] && (window['jQuery']['PUBNUB'] = PUBNUB);
+
+// For Modern JS + Testling.js - http://testling.com/
+typeof(module) !== 'undefined' && (module['exports'] = PUBNUB) && ready();
+
+var pubnubs = $('pubnubs') || 0;
+
+// LEAVE NOW IF NO PDIV.
+if (!PDIV) return;
+
+// PUBNUB Flash Socket
+css( PDIV, { 'position' : 'absolute', 'top' : -SECOND } );
+
+if ('opera' in window || attr( PDIV, 'flash' )) PDIV['innerHTML'] =
+ '';
+
+// Create Interface for Opera Flash
+PUBNUB['rdx'] = function( id, data ) {
+ if (!data) return FDomainRequest[id]['onerror']();
+ FDomainRequest[id]['responseText'] = unescape(data);
+ FDomainRequest[id]['onload']();
+};
+
+function FDomainRequest() {
+ if (!pubnubs || !pubnubs['get']) return 0;
+
+ var fdomainrequest = {
+ 'id' : FDomainRequest['id']++,
+ 'send' : function() {},
+ 'abort' : function() { fdomainrequest['id'] = {} },
+ 'open' : function( method, url ) {
+ FDomainRequest[fdomainrequest['id']] = fdomainrequest;
+ pubnubs['get']( fdomainrequest['id'], url );
+ }
+ };
+
+ return fdomainrequest;
+}
+FDomainRequest['id'] = SECOND;
+
+})();
+(function(){
+
+// ---------------------------------------------------------------------------
+// WEBSOCKET INTERFACE
+// ---------------------------------------------------------------------------
+var WS = PUBNUB['ws'] = function( url, protocols ) {
+ if (!(this instanceof WS)) return new WS( url, protocols );
+
+ var self = this
+ , url = self.url = url || ''
+ , protocol = self.protocol = protocols || 'Sec-WebSocket-Protocol'
+ , bits = url.split('/')
+ , setup = {
+ 'ssl' : bits[0] === 'wss:'
+ ,'origin' : bits[2]
+ ,'publish_key' : bits[3]
+ ,'subscribe_key' : bits[4]
+ ,'channel' : bits[5]
+ };
+
+ // READY STATES
+ self['CONNECTING'] = 0; // The connection is not yet open.
+ self['OPEN'] = 1; // The connection is open and ready to communicate.
+ self['CLOSING'] = 2; // The connection is in the process of closing.
+ self['CLOSED'] = 3; // The connection is closed or couldn't be opened.
+
+ // CLOSE STATES
+ self['CLOSE_NORMAL'] = 1000; // Normal Intended Close; completed.
+ self['CLOSE_GOING_AWAY'] = 1001; // Closed Unexpecttedly.
+ self['CLOSE_PROTOCOL_ERROR'] = 1002; // Server: Not Supported.
+ self['CLOSE_UNSUPPORTED'] = 1003; // Server: Unsupported Protocol.
+ self['CLOSE_TOO_LARGE'] = 1004; // Server: Too Much Data.
+ self['CLOSE_NO_STATUS'] = 1005; // Server: No reason.
+ self['CLOSE_ABNORMAL'] = 1006; // Abnormal Disconnect.
+
+ // Events Default
+ self['onclose'] = self['onerror'] =
+ self['onmessage'] = self['onopen'] =
+ self['onsend'] = function(){};
+
+ // Attributes
+ self['binaryType'] = '';
+ self['extensions'] = '';
+ self['bufferedAmount'] = 0;
+ self['trasnmitting'] = false;
+ self['buffer'] = [];
+ self['readyState'] = self['CONNECTING'];
+
+ // Close if no setup.
+ if (!url) {
+ self['readyState'] = self['CLOSED'];
+ self['onclose']({
+ 'code' : self['CLOSE_ABNORMAL'],
+ 'reason' : 'Missing URL',
+ 'wasClean' : true
+ });
+ return self;
+ }
+
+ // PubNub WebSocket Emulation
+ self.pubnub = PUBNUB['init'](setup);
+ self.pubnub.setup = setup;
+ self.setup = setup;
+
+ self.pubnub['subscribe']({
+ 'restore' : false,
+ 'channel' : setup['channel'],
+ 'disconnect' : self['onerror'],
+ 'reconnect' : self['onopen'],
+ 'error' : function() {
+ self['onclose']({
+ 'code' : self['CLOSE_ABNORMAL'],
+ 'reason' : 'Missing URL',
+ 'wasClean' : false
+ });
+ },
+ 'callback' : function(message) {
+ self['onmessage']({ 'data' : message });
+ },
+ 'connect' : function() {
+ self['readyState'] = self['OPEN'];
+ self['onopen']();
+ }
+ });
+};
+
+// ---------------------------------------------------------------------------
+// WEBSOCKET SEND
+// ---------------------------------------------------------------------------
+WS.prototype.send = function(data) {
+ var self = this;
+ self.pubnub['publish']({
+ 'channel' : self.pubnub.setup['channel'],
+ 'message' : data,
+ 'callback' : function(response) {
+ self['onsend']({ 'data' : response });
+ }
+ });
+};
+
+// ---------------------------------------------------------------------------
+// WEBSOCKET CLOSE
+// ---------------------------------------------------------------------------
+WS.prototype.close = function() {
+ var self = this;
+ self.pubnub['unsubscribe']({ 'channel' : self.pubnub.setup['channel'] });
+ self['readyState'] = self['CLOSED'];
+ self['onclose']({});
+};
+
+})();
diff --git a/ajax/libs/pubnub/3.4.6/pubnub.min.js b/ajax/libs/pubnub/3.4.6/pubnub.min.js
new file mode 100644
index 000000000..7c78c8b03
--- /dev/null
+++ b/ajax/libs/pubnub/3.4.6/pubnub.min.js
@@ -0,0 +1,31 @@
+// Version: 3.4.6
+(function(){
+var m=null,q=!1;function s(){return function(){}}
+window.JSON&&window.JSON.stringify||function(){function a(){try{return this.valueOf()}catch(a){return m}}function c(a){d.lastIndex=0;return d.test(a)?'"'+a.replace(d,function(a){var b=u[a];return"string"===typeof b?b:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function b(d,F){var i,k,j,l,h,r=e,g=F[d];g&&"object"===typeof g&&(g=a.call(g));"function"===typeof f&&(g=f.call(F,d,g));switch(typeof g){case "string":return c(g);case "number":return isFinite(g)?String(g):"null";case "boolean":case "null":return String(g);
+case "object":if(!g)return"null";e+=p;h=[];if("[object Array]"===Object.prototype.toString.apply(g)){l=g.length;for(i=0;i++G?G:G=1))||a};
+function ha(a,c){var b=a.join(da),d=[];if(!c)return b;K(c,function(a,b){d.push(a+"="+L(b))});return b+="?"+d.join(ea)}function ia(a,c){function b(){e+c>C()?(clearTimeout(d),d=setTimeout(b,c)):(e=C(),a())}var d,e=0;return b}function ja(a,c){var b=[];K(a||[],function(a){c(a)&&b.push(a)});return b}function ka(a,c){return a.replace(fa,function(a,d){return c[d]||a})}
+function ga(a){var c="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(a){var c=16*Math.random()|0;return("x"==a?c:c&3|8).toString(16)});a&&a(c);return c}function K(a,c){if(a&&c)if("undefined"!=typeof a[0])for(var b=0,d=a.length;b"-_.!~*'()".indexOf(a)?a:"%"+a.charCodeAt(0).toString(16).toUpperCase()}).join("")}function la(a){var c=[];K(a,function(a,d){d.g&&c.push(a)});return c.sort()}function P(){setTimeout(function(){v||(v=1,K(ba,function(a){a()}))},y)}
+if(!window.PUBNUB){var Q=function(a){return document.getElementById(a)},ma=function(a){console.error(a)},na=function(a,c){var b=[];K(a.split(/\s+/),function(a){K((c||document).getElementsByTagName(a),function(a){b.push(a)})});return b},S=function(a,c,b){K(a.split(","),function(a){function e(a){a||(a=window.event);b(a)||(a.cancelBubble=!0,a.returnValue=q,a.preventDefault&&a.preventDefault(),a.stopPropagation&&a.stopPropagation())}c.addEventListener?c.addEventListener(a,e,q):c.attachEvent?c.attachEvent("on"+
+a,e):c["on"+a]=e})},oa=function(){return na("head")[0]},T=function(a,c,b){if(b)a.setAttribute(c,b);else return a&&a.getAttribute&&a.getAttribute(c)},qa=function(a,c){for(var b in c)if(c.hasOwnProperty(b))try{a.style[b]=c[b]+(0<"|width|height|top|left|".indexOf(b)&&"number"==typeof c[b]?"px":"")}catch(d){}},ra=function(a){return document.createElement(a)},sa=function(){return U||W()?0:B()},ua=function(a){function c(a,b){M||(M=1,a||N(b),g.onerror=m,clearTimeout(V),setTimeout(function(){a&&w();var b=
+Q(t),c=b&&b.parentNode;c&&c.removeChild(b)},y))}if(U||W()){a:{var b,d,e=function(){if(!u){u=1;clearTimeout(R);try{d=JSON.parse(b.responseText)}catch(a){return l(1)}k(d)}},p=0,u=0,f=a.timeout||1E4,R=setTimeout(function(){l(1)},f),F=a.b||s(),i=a.data||{},k=a.c||s(),j="undefined"===typeof a.h,l=function(a){p||(p=1,clearTimeout(R),b&&(b.onerror=b.onload=m,b.abort&&b.abort(),b=m),a&&F())};try{b=W()||window.XDomainRequest&&new XDomainRequest||new XMLHttpRequest;b.onerror=b.onabort=function(){l(1)};b.onload=
+b.onloadend=e;j&&(b.timeout=f);i.pnsdk=ta;var h=ha(a.url,i);b.open("GET",h,j);b.send()}catch(r){l(0);U=0;a=ua(a);break a}a=l}return a}var g=ra("script"),e=a.a,t=B(),M=0,V=setTimeout(function(){c(1)},a.timeout||1E4),w=a.b||s(),f=a.data||{},N=a.c||s();window[e]=function(a){c(0,a)};a.h||(g[va]=va);g.onerror=function(){c(1)};f.pnsdk=ta;g.src=ha(a.url,f);T(g,"id",t);oa().appendChild(g);return c},wa=function(){return!("onLine"in navigator)?1:navigator.onLine},W=function(){if(!xa||!xa.get)return 0;var a=
+{id:W.id++,send:s(),abort:function(){a.id={}},open:function(c,b){W[a.id]=a;xa.get(a.id,b)}};return a},va="async",ta="PubNub-JS-Web/3.4.6",U=-1==navigator.userAgent.indexOf("MSIE 6");window.console||(window.console=window.console||{});console.log||(console.log=console.error=(window.opera||{}).postError||s());var ya,X=window.localStorage;ya={get:function(a){try{return X?X.getItem(a):-1==document.cookie.indexOf(a)?m:((document.cookie||"").match(RegExp(a+"=([^;]+)"))||[])[1]||m}catch(c){}},set:function(a,
+c){try{if(X)return X.setItem(a,c)&&0;document.cookie=a+"="+c+"; expires=Thu, 1 Aug 2030 20:00:00 UTC; path=/"}catch(b){}}};var Y={list:{},unbind:function(a){Y.list[a]=[]},bind:function(a,c){(Y.list[a]=Y.list[a]||[]).push(c)},fire:function(a,c){K(Y.list[a]||[],function(a){a(c)})}},Z=Q("pubnub")||0,za=function(a){function c(){}function b(){x.time(function(a){if(!a)for(;w.length;)w.shift()();setTimeout(b,i)})}function d(){if(!ca())for(;w.length;)w.shift()();setTimeout(d,y)}function e(a){K(la(z),function(b){(b=
+z[b])&&a(b)})}function p(a){a&&(t.i=0);!t.i&&t.length&&(t.i=1,A(t.shift()))}a.jsonp&&(U=0);var u=a.subscribe_key||"";a.uuid||ya.get(u+"uuid");a.xdr=ua;a.db=ya;a.error=ma;a._is_online=wa;a.jsonp_cb=sa;var f,R=+a.windowing||10,F=(+a.timeout||310)*y,i=(+a.keepalive||60)*y,k=a.publish_key||"",j=a.subscribe_key||"",l=a.ssl?"s":"",h="http"+l+"://"+(a.origin||"pubsub.pubnub.com"),r=E(h),g=E(h),t=[],M=0,V=0,w=[],N=0,H=0,z={},A=a.xdr,n=a.error||s(),ca=a._is_online||function(){return 1},D=a.jsonp_cb||function(){return 0},
+I=a.db||{get:s(),set:s()},J=a.uuid||I&&I.get(j+"uuid")||"",x={LEAVE:function(a,b){var c={uuid:J},d=E(h),e=D();0";
+PUBNUB.rdx=function(a,c){if(!c)return W[a].onerror();W[a].responseText=unescape(c);W[a].onload()};W.id=y}}
+var Aa=PUBNUB.ws=function(a,c){if(!(this instanceof Aa))return new Aa(a,c);var b=this,a=b.url=a||"";b.protocol=c||"Sec-WebSocket-Protocol";var d=a.split("/"),d={ssl:"wss:"===d[0],origin:d[2],publish_key:d[3],subscribe_key:d[4],channel:d[5]};b.CONNECTING=0;b.OPEN=1;b.CLOSING=2;b.CLOSED=3;b.CLOSE_NORMAL=1E3;b.CLOSE_GOING_AWAY=1001;b.CLOSE_PROTOCOL_ERROR=1002;b.CLOSE_UNSUPPORTED=1003;b.CLOSE_TOO_LARGE=1004;b.CLOSE_NO_STATUS=1005;b.CLOSE_ABNORMAL=1006;b.onclose=b.onerror=b.onmessage=b.onopen=b.onsend=
+s();b.binaryType="";b.extensions="";b.bufferedAmount=0;b.trasnmitting=q;b.buffer=[];b.readyState=b.CONNECTING;if(!a)return b.readyState=b.CLOSED,b.onclose({code:b.CLOSE_ABNORMAL,reason:"Missing URL",wasClean:!0}),b;b.e=PUBNUB.init(d);b.e.j=d;b.j=d;b.e.subscribe({restore:q,channel:d.channel,disconnect:b.onerror,reconnect:b.onopen,error:function(){b.onclose({code:b.CLOSE_ABNORMAL,reason:"Missing URL",wasClean:q})},callback:function(a){b.onmessage({data:a})},connect:function(){b.readyState=b.OPEN;b.onopen()}})};
+Aa.prototype.send=function(a){var c=this;c.e.publish({channel:c.e.j.channel,message:a,callback:function(a){c.onsend({data:a})}})};
+})();
diff --git a/ajax/libs/pubnub/package.json b/ajax/libs/pubnub/package.json
index 554704310..1079ddc99 100644
--- a/ajax/libs/pubnub/package.json
+++ b/ajax/libs/pubnub/package.json
@@ -1,7 +1,7 @@
{
"name": "pubnub",
"filename": "pubnub.min.js",
- "version": "3.4.5",
+ "version": "3.4.6",
"description": "PubNub, a new kind of Cloud-Hosted Broadcasting Service for Mass Communication.",
"homepage": "http://www.pubnub.com/",
"keywords": [