').append(date_stamp.clone()).html()
};
} else {
//$('
').appendTo(message_log);
return {replace_last: false, html: ''};
}
},
// === //private// {{{AjaxIM.}}}**{{{_addError(chatbox, error)}}}** //
//
// Adds an error to a chatbox. These are generally inserted after
// a user sends a message unsuccessfully. If an error message
// was already added, another one will be added anyway.
//
// ==== Parameters ====
// * {{{chatbox}}} refers to the jQuery-selected chatbox DOM element.
// * {{{error}}} is the error message string.
// * {{{time}}} is the date/time the error occurred. It is specified in
// milliseconds since the Unix Epoch. This is //only// defined when
// errors are being restored from storage; if not specified, the current
// computer time will be used.
_addError: function(chatbox, error, time) {
var message_log = $(chatbox).find('.imjs-msglog');
var error_item =
$('.imjs-tab.imjs-default .imjs-chatbox .imjs-msglog .imjs-error').clone();
var error_item_time = error_item.find('.imjs-msg-time');
if(error_item_time.length) {
if(!time)
time = (new Date()).getTime();
error_item_time.html(dateFormat(time, error_item_time.html()));
}
error_item.find('.imjs-error-error').html(error);
error_item.appendTo(message_log);
message_log[0].scrollTop = message_log[0].scrollHeight;
return {
replace_last: false,
html: jQuery('
').append(error_item.clone()).html()
};
},
// === //private// {{{AjaxIM.}}}**{{{_addMessage(ab, chatbox, username, message, time)}}}** //
//
// Adds a message to a chatbox. Depending on the {{{ab}}} value,
// the color of the username may change as a way of visually
// identifying users (however, this depends on the theme's CSS).
// A timestamp is added to the message, and the chatbox is scrolled
// to the bottom, such that the new message is visible.
//
// Messages will be automatically tag-escaped, so as to prevent
// any potential cross-site scripting problems. Additionally,
// URLs will be automatically linked.
//
// ==== Parameters ====
// * {{{ab}}} refers to whether the user is "a" or "b" in a conversation.
// For the general case, "you" are "a" and "they" are "b".
// * {{{chatbox}}} refers to the jQuery-selected chatbox DOM element.
// * {{{username}}} is the username of the user who sent the message.
// * {{{time}}} is the time the message was sent in milliseconds since
// the Unix Epoch. This is //only// defined when messages are being
// restored from storage. For new messages, the current computer
// time is automatically used.
_addMessage: function(ab, chatbox, username, message, time) {
var last_message = chatbox.find('.imjs-msglog > *:last-child');
if(last_message.hasClass('imjs-msg-' + ab)) {
// Last message was from the same person, so let's just add another imjs-msg-*-msg
var message_container = (last_message.hasClass('imjs-msg-' + ab + '-container')
? last_message
: last_message.find('.imjs-msg-' + ab + '-container'));
var single_message =
$('.imjs-tab.imjs-default .imjs-chatbox .imjs-msglog .imjs-msg-' + ab + '-msg')
.clone().appendTo(message_container);
single_message.html(single_message.html().replace('{username}', username));
} else if(!last_message.length || !last_message.hasClass('imjs-msg-' + ab)) {
var message_group = $('.imjs-tab.imjs-default .imjs-chatbox .imjs-msg-' + ab)
.clone().appendTo(chatbox.find('.imjs-msglog'));
message_group.html(message_group.html().replace('{username}', username));
var single_message = message_group.find('.imjs-msg-' + ab + '-msg');
}
// clean up the message
message = message.toString().replace(//g, '>')
.replace(/(^|.*)\*([^*]+)\*(.*|$)/, '$1
$2$3');
// autolink URLs
message = message.replace(
new RegExp('([A-Za-z][A-Za-z0-9+.-]{1,120}:[A-Za-z0-9/]' +
'(([A-Za-z0-9$_.+!*,;/?:@&~=-])|%[A-Fa-f0-9]{2}){1,333}' +
'(#([a-zA-Z0-9][a-zA-Z0-9$_.+!*,;/?:@&~=%-]{0,1000}))?)', 'g'),
'
$1');
// insert the message
single_message.html(single_message.html().replace('{message}', message));
// set the message time
var msgtime = single_message.find('.imjs-msg-time');
if(!time)
time = new Date();
if(typeof time != 'string')
time = dateFormat(time, msgtime.html());
msgtime.html(time);
var msglog = chatbox.find('.imjs-msglog');
msglog[0].scrollTop = msglog[0].scrollHeight;
return {
replace_last : !!message_container,
html: jQuery('
').append(
message_container
? last_message.clone()
: message_group.clone()
).html()
};
},
_store: function(username, msg) {
if(!msg.html.length) return;
if(!this.chatstore) this.chatstore = {};
if(!(username in this.chatstore)) {
this.chatstore[username] = [];
} else if(this.chatstore[username].length > 300) {
// If the chat store gets too long, it becomes slow to load.
this.chatstore[username].shift();
}
if(msg.replace_last)
this.chatstore[username].pop();
this.chatstore[username].push(msg.html);
store.set(this.username + '-chats', this.chatstore);
},
// === //private// {{{AjaxIM.}}}**{{{_friendUpdate(friend, status, statusMessage)}}}** ===
//
// Called when a friend's status is updated. This function will update all locations
// where a status icon is displayed (chat tab, friends list), as well as insert
// a notification, should a chatbox be open.
//
// ==== Parameters ====
// * {{{friend}}} is the username of the friend.
// * {{{status}}} is the new status code. See {{{AjaxIM.statuses}}} for a list of available
// codes. //Note: If an invalid status is specified, no action will be taken.//
// * {{{statusMessage}}} is a message that was, optionally, specified by the user. It will be
// used should "you" send the user an IM while they are away, or if their status is viewed
// in another way (such as via the friends list [**not yet implemented**]).
_friendUpdate: function(friend, status, statusMessage) {
if(this.chats[friend]) {
var tab = this.chats[friend].parents('.imjs-tab');
var tab_class = 'imjs-tab';
if(tab.data('state') == 'active') tab_class += ' imjs-selected';
tab_class += ' imjs-' + status;
tab.attr('class', tab_class);
// display the status in the chatbox
var date_stamp =
$('.imjs-tab.imjs-default .imjs-chatbox .imjs-msglog .imjs-date').clone();
var date_stamp_time = date_stamp.find('.imjs-msg-time');
if(date_stamp_time.length)
date_stamp_time.html(dateFormat(date_stamp_time.html()));
var date_stamp_date = date_stamp.find('.imjs-date-date').html(
AjaxIM.l10n[
'chat' + status.toUpperCase().slice(0, 1) + status.slice(1)
].replace(/%s/g, friend));
var msglog = this.chats[friend].find('.imjs-msglog');
date_stamp.appendTo(msglog);
msglog[0].scrollTop = msglog[0].scrollHeight;
}
if (!this.friends[friend]) {
this.addFriend(friend, [status, statusMessage], 'Friends');
}
if(this.friends[friend]) {
var friend_id = 'imjs-friend-' + md5.hex(friend + this.friends[friend].group);
$('#' + friend_id).attr('class', 'imjs-friend imjs-' + status);
$('#' + friend_id).find('.imjs-friend-status')
.html(statusMessage)
.attr('status', statusMessage);
if(status == 'offline') {
$('#' + friend_id + ':visible').slideUp();
$('#' + friend_id + ':hidden').hide();
} else if(!$('#' + friend_id + ':visible').length) {
$('#' + friend_id).slideDown();
}
this.friends[friend].status = [status, statusMessage];
this._updateFriendCount();
}
},
// === //private// {{{AjaxIM.}}}**{{{_notConnected()}}}** ===
//
// Puts the user into a visible state of disconnection. Sets the
// friends list to "not connected" and empties it; disallows new messages
// to be sent.
_notConnected: function() {
$('#imjs-friends')
.addClass('imjs-not-connected')
.unbind('click', this.activateTab);
if($('#imjs-friends').hasClass('imjs-selected'))
this.activateTab($('#imjs-friends'));
},
_showReconnect: function() {
$('#imjs-reconnect').show();
},
// === {{{AjaxIM.}}}**{{{send(to, message)}}}** ===
//
// Sends a message to another user. The message will be added
// to the chatbox before it is actually sent, however, if an
// error occurs during sending, that will be indicated immediately
// afterward.
//
// After sending the message, one of three status codes should be
// returned as a JSON object, e.g. {{{{r: 'code'}}}}:
// * {{{ok}}} — Message was sent successfully.
// * {{{offline}}} — The user is offline or unavailable to
// receive messages.
// * {{{error}}} — a problem occurred, unrelated to the user
// being unavailable.
//
// ==== Parameters ====
// * {{{to}}} is the username of the recipient.
// * {{{message}}} is the content to be sent.
send: function(username, body) {
if(!body) return;
var self = this;
if(this.chats[username]) {
// possibly add a datestamp
this._store(username, this._addDateStamp(this.chats[username]));
this._store(username,
this._addMessage('a', this.chats[username],
this.username, body));
}
$(this).trigger('sendingMessage', [username, body]);
var event = {type: 'message', from: this.username, to: username, body: body};
this.sendEvent(event, function(result) {
if(result._status.sent) {
$(self).trigger('sendMessageSuccessful', [username, body]);
} else if(result.type == 'error') {
if(result.error == 'not online')
$(self).trigger('sendMessageFailed', ['offline', username, body]);
else
$(self).trigger('sendMessageFailed', [result.error, username, body]);
}
}, function(error) {
self._notConnected();
var error = self._addError(
self.chats[username],
'You are currently not connected or the ' +
'server is not available. Please ensure ' +
'that you are signed in and try again.');
self._store(error);
$(self).trigger('sendMessageFailed',
['not connected', username, body]);
});
},
// === {{{AjaxIM.}}}**{{{status(s, message)}}}** ===
//
// Sets the user's status and status message. It is possible to not
// set a status message by setting it to an empty string. The status
// will be sent to the server, where upon the server will broadcast
// the update to all individuals with "you" on their friends list.
//
// ==== Parameters ====
// * {{{s}}} is the status code, as defined by {{{AjaxIM.statuses}}}.
// * {{{message}}} is the custom status message.
status: function(value, message) {
var self = this;
// update status icon(s)
if(!~this.statuses.indexOf(value))
return;
// check if selected before writing over the class!
$(this.statuses).each(function() {
$('#imjs-friends').removeClass('imjs-' + this);
});
$('#imjs-friends').addClass('imjs-' + value);
$(this).trigger('changingStatus', [value, message]);
if(value == 'offline') {
self._notConnected();
self._showReconnect();
store.set(this.username + '-offline', true);
self.offline = true;
$('.imjs-input').attr('disabled', true);
AjaxIM.post(
this.actions.signoff,
{},
function(result) {
if(result.type == 'success')
$(self).trigger('changeStatusSuccessful',
[value, null]);
},
function(error) {
$(self).trigger('changeStatusFailed',
['not connected', value, null]);
}
);
} else {
var event = {type: 'status', status: value, message: message};
this.sendEvent(event, function(result) {
if(result._status.send) {
$(self).trigger('sendMessageSuccessful', [username, body]);
} else if(result.type == 'error') {
if(result.error == 'not online')
$(self).trigger('sendMessageFailed', ['offline', username, body]);
else
$(self).trigger('sendMessageFailed', [result.error, username, body]);
}
}, function(error) {
self._notConnected();
var error = self._addError(
self.chats[username],
'You are currently not connected or the ' +
'server is not available. Please ensure ' +
'that you are signed in and try again.');
self._store(error);
$(self).trigger('sendMessageFailed',
['not connected', username, body]);
});
}
},
// === {{{AjaxIM.}}}**{{{statuses}}}** ===
//
// These are the available status codes and their associated identities:
// * {{{offline}}} (0) — Only used when signing out/when another
// user has signed out, as once this status is set, the user is removed
// from the server and friends will be unable to contact the user.
// * {{{available}}} (1) — The user is online and ready to be messaged.
// * {{{away}}} (2) — The user is online but is not available. Others
// may still contact this user, however, the user may not respond. Anyone
// contacting an away user will receive a notice stating that the user is away,
// and (if one is set) their custom status message.
// * {{{invisible}}} (3; **not yet implemented**) — The user is online,
// but other users are made unaware, and the user will be represented
// as being offline. It is still possible to contact this user, and for this
// user to contact others; no status message or notice will be sent to others
// messaging this user.
statuses: ['offline', 'available', 'away'],
// === {{{AjaxIM.}}}**{{{initTabs()}}}** ===
//
// Setup the footer bar and enable tab actions. This function
// uses {{{jQuery.live}}} to set hooks on any bar tabs created
// in the future.
initTabBar: function() {
var self = this;
// Set up your standard tab actions
$(document)
.on('click', '.imjs-tab', function() {
return self.activateTab.call(self, $(this));
});
$(document)
.on('click', '.imjs-tab .imjs-close', function() {
return self.closeTab.call(self, $(this));
});
// Set up the friends list actions
$(document).click(function(e) {
if(~['imjs-friends'].indexOf(e.target.id) ||
$(e.target).parents('#imjs-friends').length) {
return;
}
if($('#imjs-friends').data('state') == 'active')
self.activateTab.call(self, $('#imjs-friends'));
else if($('#imjs-status').data('state') == 'active')
self.activateTab.call(self, $('#imjs-status'));
});
$('#imjs-friends')
.data('state', 'minimized')
.click(function(e) {
if(!$(this).hasClass('imjs-not-connected') &&
e.target.id != 'imjs-friends-panel' &&
!$(e.target).parents('#imjs-friends-panel').length)
self.activateTab.call(self, $(this));
})
.mouseenter(function() {
if($(this).hasClass('imjs-not-connected')) {
$('.imjs-tooltip')
.css('display', 'block')
.find('p')
.html(AjaxIM.l10n.notConnectedTip);
var tip_left = $(this).offset().left -
$('.imjs-tooltip').outerWidth() +
($(this).outerWidth() / 2);
var tip_top = $(this).offset().top -
$('.imjs-tooltip').outerHeight(true);
$('.imjs-tooltip').css({
left: tip_left,
top: tip_top
});
}
})
.mouseleave(function() {
if($(this).hasClass('imjs-not-connected')) {
$('.imjs-tooltip').css('display', '');
}
});
$('#imjs-friends-panel').css('display', 'none');
},
// === {{{AjaxIM.}}}**{{{activateTab()}}}** ===
//
// Activate a tab by setting it to the 'active' state and
// showing any related chatbox. If a chatbox is available
// for this tab, also focus the input box.
//
// //Note:// {{{this}}}, here, refers to the tab DOM element.
activateTab: function(tab) {
var chatbox = tab.find('.imjs-chatbox') || false,
input;
if(tab.data('state') != 'active') {
if(tab.attr('id') != 'imjs-friends') {
$('#imjs-bar > li')
.not(tab)
.not('#imjs-friends, .imjs-scroll, .imjs-default')
.add(tab.attr('id') == 'imjs-status' ? '#imjs-friends' : '')
.removeClass('imjs-selected')
.each(function() {
var self = $(this);
if(self.data('state') != 'closed') {
self.data('state', 'minimized');
var chatbox = self.find('.imjs-chatbox');
if(chatbox.length)
chatbox.css('display', 'none');
}
});
} else {
$('#imjs-status')
.removeClass('imjs-selected')
.data('state', 'minimized')
.find('.imjs-chatbox')
.css('display', 'none');
}
if(chatbox && chatbox.css('display') == 'none')
chatbox.css('display', '');
// set the tab to active...
tab.addClass('imjs-selected').data('state', 'active');
// ...and hide and reset the notification icon
tab.find('.imjs-notification').css('display', 'none')
.data('count', 0);
if(chatbox && (username = chatbox.data('username')))
store.set(this.username + '-activeTab', username);
$(this).trigger('tabToggled', ['activated', tab]);
} else {
tab.removeClass('imjs-selected').data('state', 'minimized');
if(chatbox && chatbox.css('display') != 'none')
chatbox.css('display', 'none');
store.set(this.username + '-activeTab', '');
$(this).trigger('tabToggled', ['minimized', tab]);
}
if(chatbox) {
if((input = chatbox.find('.imjs-input')).length &&
!input.data('height')) {
input.height(0);
if(input[0].scrollHeight > input.height() ||
input[0].scrollHeight < input.height()) {
input.height(input[0].scrollHeight);
}
// store the height for resizing later
if (!input.height()) {
input.height(16);
}
input.data('height', input.height());
}
try {
var msglog = chatbox.find('.imjs-msglog');
msglog[0].scrollTop = msglog[0].scrollHeight;
} catch(e) {}
try { chatbox.find('.imjs-input').focus(); } catch(e) {}
}
},
// === {{{AjaxIM.}}}**{{{closeTab()}}}** ===
//
// Close a tab and hide any related chatbox, such that
// the chatbox can not be reopened without reinitializing
// the tab.
//
// //Note:// {{{this}}}, here, refers to the tab DOM element.
closeTab: function(tab) {
tab = tab.parents('.imjs-tab');
tab.css('display', 'none')
.removeClass('imjs-selected')
.data('state', 'closed');
delete this.chatstore[tab.find('.imjs-chatbox').data('username')];
store.set(this.username + '-chats', this.chatstore);
$(this).trigger('tabToggled', ['closed', tab]);
this._scrollers();
return false;
},
// === {{{AjaxIM.}}}**{{{addTab(label, action, closable)}}}** ===
//
// Adds a tab to the tab bar, with the label {{{label}}}. When
// clicked, it will call a callback function, {{{action}}}. If
// {{{action}}} is a string, it is assumed that the string is
// referring to a chatbox ID.
//
// ==== Parameters ====
// * {{{label}}} is the text that will be displayed on the tab.\\
// * {{{action}}} is the callback function, if it is a non-chatbox
// tab, or a string if it //is// a chatbox tab.\\
// * {{{closable}}} is a boolean value that determines whether or not
// it is possible for a user to close this tab.
//
// //Note:// New tabs are given an automatically generated ID
// in the format of {{{#imjs-tab-[md5 of label]}}}.
addTab: function(label, action, closable) {
var tab = $('.imjs-tab.imjs-default').clone().insertAfter('#imjs-scroll-left');
tab.removeClass('imjs-default')
.attr('id', 'imjs-tab-' + md5.hex(label))
.html(tab.html().replace('{label}', label))
.data('state', 'minimized');
var notification = tab.find('.imjs-notification');
notification.css('display', 'none')
.data('count', 0)
.data('default-text', notification.html())
.html(notification.html().replace('{count}', '0'));
if(closable === false)
tab.find('.imjs-close').eq(0).remove();
if(typeof action != 'string') {
tab.find('.imjs-chatbox').remove();
tab.click(action);
}
return tab;
},
// === {{{AjaxIM.}}}**{{{notification(tab)}}}** ===
//
// Displays a notification on a tab. Generally, this is called when
// a tab is minimized to let the user know that there is an update
// for them. The way the notification is displayed depends on the
// theme CSS.
//
// ==== Parameters ====
// * {{{tab}}} is the jQuery-selected tab DOM element.
notification: function(tab) {
var notify = tab.find('.imjs-notification');
var notify_count = notify.data('count') + 1;
notify.data('count', notify_count)
.html(notify.data('default-text').replace('{count}', notify_count))
.css('display', '');
},
// === //private// {{{AjaxIM.}}}**{{{_scrollers()}}}** ===
//
// Document me!
_scrollers: function() {
var needScrollers = false;
$('#imjs-scroll-left').nextAll('.imjs-tab')
.filter(function() {
return $(this).data('state') != 'closed';
})
.each(function(i, tab) {
tab = $(tab).css('display', '');
var tab_pos = tab.position();
if(tab_pos.top >= $('#imjs-bar').height() ||
tab_pos.left < 0 ||
tab_pos.right > $(document).width()) {
$('.imjs-scroll').css('display', '');
tab.css('display', 'none');
needScrollers = true;
}
});
if(!needScrollers) {
$('.imjs-scroll').css('display', 'none');
}
if($('#imjs-scroll-left').css('display') != 'none' &&
$('#imjs-scroll-right').position().top >= $('#imjs-bar').height()) {
$('#imjs-bar li.imjs-tab:visible').slice(-1).css('display', 'none');
}
if($('#imjs-bar li.imjs-tab:visible').length) {
while($('.imjs-selected').css('display') == 'none')
$('#imjs-scroll-right').click();
}
this._scrollerIndex();
},
_scrollerIndex: function() {
var hiddenRight = $('#imjs-bar li.imjs-tab:visible').slice(-1)
.nextAll('#imjs-bar li.imjs-tab:hidden')
.not('.imjs-default')
.filter(function() {
return $(this).data('state') != 'closed'
}).length;
var hiddenLeft = $('#imjs-bar li.imjs-tab:visible').eq(0)
.prevAll('#imjs-bar li.imjs-tab:hidden')
.not('.imjs-default')
.filter(function() {
return $(this).data('state') != 'closed'
}).length;
$('#imjs-scroll-left').html(hiddenLeft);
$('#imjs-scroll-right').html(hiddenRight);
},
unconfirmedEvents: {},
eventId: 1,
createEvent: function() {
var event = {};
event.id = this.eventId++;;
this.unconfirmedEvents[event.id] = evt;
},
sendEvent: function(event, successFunc, failureFunc) {
event.id = this.eventId++;
var evt = $.extend({}, event);
evt['_status'] = {
successFunc: successFunc,
failureFunc: failureFunc
};
this.unconfirmedEvents[event.id] = evt;
if (this.socket) {
this.socket.emit('server', event);
} else {
var self = this;
var url = null;
switch (event.type) {
case 'message':
url = this.actions.send;
break;
case 'status':
url = this.actions.status;
break;
case 'signoff':
url = this.actions.signoff;
break;
default:
break;
}
AjaxIM.post(url, event,
function(result) {
if (result) {
for (var e=0; e < result.length; ++e) {
self.dispatchEvent(events[e]);
}
}
},
function(error) {
if (self.unconfirmedEvents[event.id]) {
event = self.unconfirmedEvents[event.id];
event['_status']['sent'] = false;
self.dispatchEvent(event);
}
}
);
}
},
dispatchEvent: function(event) {
if (event.id && this.unconfirmedEvents[event.id]) {
event['_status'] = $.extend({}, this.unconfirmedEvents[event.id]['_status'], event['_status']);
delete this.unconfirmedEvents[event.id];
if (event['_status']['sent']) {
event['_status']['successFunc'](event);
} else {
event['_status']['failureFunc'](event);
}
} else {
this.triggerEvent(event);
}
},
// poor man's Backbone.js Events
eventHandlers: {},
/**
* Add a callback to listen for an event type.
*/
onEvent: function(eventType, callback) {
if (!this.eventHandlers[eventType]) {
this.eventHandlers[eventType] = [];
}
this.eventHandlers[eventType].push(callback);
},
/**
* Trigger an event on all interested callbacks.
*/
triggerEvent: function(event) {
if (this.eventHandlers[event.type]) {
for (var e=0; e < this.eventHandlers[event.type].length; ++e) {
this.eventHandlers[event.type][e].call(this, event);
}
}
}
})
// == Static functions and variables ==
//
// The following functions and variables are available outside of an initialized
// {{{AjaxIM}}} object.
// === {{{AjaxIM.}}}**{{{client}}}** ===
//
// Once {{{AjaxIM.init()}}} is called, this will be set to the active AjaxIM
// object. Only one AjaxIM object instance can exist at a time. This variable
// can and should be accessed directly.
AjaxIM.client = null;
// === {{{AjaxIM.}}}**{{{init(options, actions)}}}** ===
//
// Initialize the AjaxIM client object and engine. Here, you can define your
// options and actions as outlined at the top of this documentation.
//
// ==== Parameters ====
// * {{{options}}} is the hash of custom settings to initialize Ajax IM with.
// * {{{actions}}} is the hash of any custom action URLs.
AjaxIM.init = function(options, actions) {
if(!AjaxIM.client)
AjaxIM.client = new AjaxIM(options, actions);
return AjaxIM.client;
};
// === {{{AjaxIM.}}}**{{{request(url, data, successFunc, failureFunc)}}}** ===
//
// Wrapper around {{{$.jsonp}}}, the JSON-P library for jQuery, and {{{$.ajax}}},
// jQuery's ajax library. Allows either function to be called, automatically,
// depending on the request's URL array (see {{{AjaxIM.actions}}}).
//
// ==== Parameters ====
// {{{url}}} is the URL of the request.
// {{{data}}} are any arguments that go along with the request.
// {{{success}}} is a callback function called when a request has completed
// without issue.
// {{{_ignore_}}} is simply to provide compatability with {{{$.post}}}.
// {{{failure}}} is a callback function called when a request hasn't not
// completed successfully.
AjaxIM.post = function(url, data, successFunc, failureFunc, urlnoop) {
AjaxIM.request(url, 'POST', data, successFunc, failureFunc, urlnoop);
};
AjaxIM.get = function(url, data, successFunc, failureFunc, urlnoop) {
AjaxIM.request(url, 'GET', data, successFunc, failureFunc, urlnoop);
};
AjaxIM.request = function(url, type, data, successFunc, failureFunc, noopurl) {
var errorTypes = ['timeout', 'error', 'notmodified', 'parseerror'];
if(typeof failureFunc != 'function')
failureFunc = function(){};
var jsonp = (url.substring(0, 1) !== '/');
var success = false;
data['sessionid'] = cookies.get('sessionid');
$.ajax({
url: url,
data: data,
dataType: jsonp? 'jsonp': 'json',
type: type,
cache: false,
timeout: 299000
}).done(function(data) {
success = true;
_dbg(JSON.stringify(data));
successFunc(data);
}).fail(function(jqXHR, textStatus) {
_dbg(textStatus);
failureFunc(textStatus);
});
if (jsonp) {
setTimeout(function() {
var failfn = function() {
if (!success) {
var textStatus = 'error';
_dbg(textStatus);
failureFunc(textStatus);
}
};
if (noopurl) {
var noopfn = function() {
var noopdone = false;
var event = {type: 'noop'};
$.ajax({
url: noopurl,
data: event,
dataType: 'jsonp',
type: type,
cache: false,
timeout: 299000
}).done(function(data) {
noopdone = true;
if (!success) {
setTimeout(noopfn, 3000);
}
}).fail(function(jqXHR, textStatus) {
// since JSONP, never called
});
setTimeout(function() {
if (!noopdone) {
failfn();
}
}, 3000);
};
noopfn();
} else {
failfn();
}
}, 3000);
}
// This prevents Firefox from spinning indefinitely
// while it waits for a response.
/*
if(url == 'jsonp' && $.browser.mozilla) {
$.jsonp({
'url': 'about:',
timeout: 0
});
}
*/
};
// === {{{AjaxIM.}}}**{{{incoming(data)}}}** ===
//
// Never call this directly. It is used as a connecting function between
// client and server for Comet.
//
// //Note:// There are two {{{AjaxIM.incoming()}}} functions. This one is a
// static function called outside of the initialized AjaxIM object; the other
// is only called within the initalized AjaxIM object.
AjaxIM.incoming = function(data) {
if(!AjaxIM.client)
return false;
if(data.length)
AjaxIM.client._parseMessages(data);
};
AjaxIM.eventID = 1;
// === {{{AjaxIM.}}}**{{{l10n}}}** ===
//
// Text strings used by Ajax IM. Should you want to translate Ajax IM into
// another language, merely change these strings.
//
// {{{%s}}} denotes text that will be automatically replaced when the string is
// used.
AjaxIM._ = function(str) {
if(str in AjaxIM.l10n) return AjaxIM.l10n[str];
return str;
};
AjaxIM.l10n = {
dayNames: [
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
],
monthNames: [
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
"January", "February", "March", "April", "May", "June", "July", "August", "September",
"October", "November", "December"
],
chatOffline: '%s signed off.',
chatAvailable: '%s became available.',
chatAway: '%s went away.',
notConnected: 'You are currently not connected or the server is not available. ' +
'Please ensure that you are signed in and try again.',
notConnectedTip: 'You are currently not connected.',
defaultAway: 'I\'m away.'
};
AjaxIM.debug = true;
function _dbg(msg) {
if(AjaxIM.debug && window.console) console.log(msg);
}
function uid(n){
var chars='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', nn='';
for(var c=0; c < n; c++){
nn += chars.substr(0|Math.random() * chars.length, 1);
}
return nn;
}