diff --git a/addons/web_chat/__init__.py b/addons/web_chat/__init__.py
deleted file mode 100644
index ee5959455ad..00000000000
--- a/addons/web_chat/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-import controllers
diff --git a/addons/web_chat/__openerp__.py b/addons/web_chat/__openerp__.py
deleted file mode 100644
index 109fe54f345..00000000000
--- a/addons/web_chat/__openerp__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "name": "Web Chat",
- "category": "Hidden",
- "description":
- """
- OpenERP Web chat module.
- """,
- "version": "2.0",
- "depends": ['web'],
- "js": [
- 'static/lib/AjaxIM/js/jquery.jsonp-1.1.0.js',
- 'static/lib/AjaxIM/js/jquery.jstore-all-min.js',
- 'static/lib/AjaxIM/js/jquery.md5.js',
- 'static/lib/AjaxIM/js/im.js',
- 'static/src/js/web_chat.js'
- ],
- "css": [],
- 'active': False,
- 'installable': False,
-}
diff --git a/addons/web_chat/controllers/__init__.py b/addons/web_chat/controllers/__init__.py
deleted file mode 100644
index 8ee9bae18d9..00000000000
--- a/addons/web_chat/controllers/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-import main
diff --git a/addons/web_chat/controllers/main.py b/addons/web_chat/controllers/main.py
deleted file mode 100644
index 87d64806180..00000000000
--- a/addons/web_chat/controllers/main.py
+++ /dev/null
@@ -1,215 +0,0 @@
-# -*- coding: utf-8 -*-
-import time
-
-import simplejson
-import web.common.http as openerpweb
-import logging
-
-_logger = logging.getLogger(__name__)
-
-#----------------------------------------------------------
-# OpenERP Web ajaxim Controllers
-#----------------------------------------------------------
-class PollServerMessageQueue(object):
- def __init__(self):
- # message queue
- self.messages = []
- # online users
- self.users = {}
- # should contains: {
- # 'user1234' : { s:1, m:"status message", timestamp: last_contact_timestamp }
- # }
- def userlist(self, req):
- userlist = [users for users in req.applicationsession['users']]
-
-# userlist = [
-# {"u": "Guest130205108745.47", "s": {"s": 1, "m": ""}, "g": "Users"},
-# {"u": "Guest130209838956.76", "s": {"s": 1, "m": ""}, "g": "Users"},
-# ]
-
- return userlist
-
- def write(self, m_type, m_from, m_to, m_message, m_group):
- self.messages.append({'type': m_type, 'from': m_from, 'to': m_to, 'message': m_message, 'group': m_group})
- # when status message update users
- pass
-
- def read(self, recipient, timestamp):
- for msg in self.messages:
- if msg['to'] == recipient:
- return self.messages
-
- def gc(self):
- # remove message older than 300s from self.l
- # remove dead users from self.users
- pass
-
-class PollServer(openerpweb.Controller):
- _cp_path = "/web_chat/pollserver"
-
- @openerpweb.httprequest
- def login(self, req, **kw):
-
- """
- --> POST http://ajaxim.com/wp-content/plugins/im/ajaxim.php/login
- Form Data
- username:""
- password:"d41d8cd98f00b204e9800998ecf8427e"
- <-- 200 OK
- Content-Type:text/html
-
- {
- "r":"logged in",
- "u":"Guest130213866190.85",
- "s":"f9e1811536f19ad5b9e00376f9ff1532",
- "f":[
- {"u":"Guest130205108745.47","s":{"s":1,"m":""},"g":"Users"},
- {"u":"Guest130209838956.76","s":{"s":1,"m":""},"g":"Users"},
- ]
- }
- """
- mq = req.applicationsession.setdefault("web_chat", PollServerMessageQueue())
- mq.messages = []
-
- #r = 'logged in'
- #u = generate random.randint(0,2**32)
- #f = mq.userlist()
-
-# username = 'Guest'+ str(random.randint(0, 2**32))
-#
-# if not req.applicationsession.get('users'):
-# req.applicationsession['users'] = [{'u': username, 's':{'s':1, 'm':''}, 'g':'Users'}]
-# else:
-# req.applicationsession['users'].append({'u': username, 's':{'s':1, 'm':''}, 'g':'Users'})
- req.applicationsession['users'] = [{'u': 'Guest1', 's':{'s':1, 'm':'111'}, 'g':'Users'},
- {'u': 'Guest2', 's':{'s':1, 'm':'222'}, 'g':'Users'},
- {'u': 'Guest3', 's':{'s':1, 'm':'333'}, 'g':'Users'}]
-
- # Temporary Guest1 is my current user
- req.applicationsession['current_user'] = 'Guest1'
-
-
- return simplejson.dumps({'r': 'logged in', 'u': 'Guest1', 's': 'f9e1811536f19ad5b9e00376f9ff1532',
- 'f': [{'u': 'Guest1', 's':{'s':1, 'm':'111'}, 'g':'Users'},
- {'u': 'Guest2', 's':{'s':1, 'm':'222'}, 'g':'Users'},
- {'u': 'Guest3', 's':{'s':1, 'm':'333'}, 'g':'Users'}]})
-
- @openerpweb.httprequest
- def logout(self, req):
- """
- --> GET http://im.ajaxim.com/logout
- { "r":"logged out" }
- """
-
- @openerpweb.httprequest
- def poll(self, req, **kw):
- """
- --> GET http://im.ajaxim.com/poll?callback=jsonp1302138663582&_1302138663582=
- <-- 200 OK
- Content-Type:text/html
-
- noop:
- jsonp1302138663582([]);
-
- roster user online:
- jsonp1302140366243([{"t":"s","s":"Guest130214038974.31","r":"all","m":"1:","g":"Users"}]);
-
- roster user left:
- jsonp1302140441577([{"t":"s","s":"Guest130214038974.31","r":"","m":"0:"}]);
-
- receive message:
- jsonp1302140191599([{"t":"m","s":"Guest130214008855.5","r":"Guest130214013134.26","m":"xxxxxx"}]);
-
- ('t' => $msg->type, 's' => $msg->from, 'r' => $msg->to, 'm' => $msg->message )
- mag type s or m
- echo ''
-
- """
- mq = req.applicationsession.setdefault("web_chat", PollServerMessageQueue())
-
- # Method: Long Poll
-
-
- msg = '[]'
-
- for i in range(5):
- received_msg = mq.read('Guest1', i)
- if received_msg:
- msg = self._pollParseMessages(received_msg)
- mq.messages = []
- return '%s'%kw.get('callback', '') + '(' + str(msg) + ');'
- else:
- msg = '[]'
- time.sleep(2)
- return '%s'%kw.get('callback', '') + '(' + str(msg) + ');'
-
- @openerpweb.httprequest
- def send(self, req, **kw):
- """
- --> GET http://im.ajaxim.com/send?callback=jsonp1302139980022&to=Guest130205108745.47&message=test&_1302139980022=
- callback: jsonp1302139980022
- to: Guest130205108745.47
- message: test
- _1302139980022:
-
- <-- 200 OK
- Content-Type:text/html
-
- return array('r' => 'sent');
- return array('r' => 'error', 'e' => 'no session found');
- return array('r' => 'error', 'e' => 'no_recipient');
- return array('r' => 'error', 'e' => 'send error');
-
- """
-
- to = kw.get('to')
- message = kw.get('message')
-
- mq = req.applicationsession.setdefault("web_chat", PollServerMessageQueue())
-
-
- if not req.applicationsession['current_user']:
- return dict(r='error', e='no session found')
-
- if not to:
- return dict(r='error', e='no_recipient')
-
- if message:
- mq.write(m_type="m", m_from=req.applicationsession['current_user'], m_to=to, m_message=message, m_group="Users")
- return '%s'%kw.get('callback', '') + '(' + simplejson.dumps({'r': 'sent'}) + ')'
- else:
- return {'r': 'error', 'e': 'send error'}
-
- @openerpweb.httprequest
- def status(self, req, **kw):
- """
- --> GET status call
- const Offline = 0;
- const Available = 1;
- const Away = 2;
- const Invisible = 3;
-
- <-- 200 OK
- Content-Type:text/html
-
- return array('r' => 'status set');
- return array('r' => 'error', 'e' => 'no session found');
- return array('r' => 'error', 'e' => 'status error');
- """
- mq = req.applicationsession.setdefault("web_chat", PollServerMessageQueue())
-
- _logger.debug("======== chat status ======== %s", kw)
- # mq.write()
- return {"action": ""}
-
- def _pollParseMessages(self, messages):
- msg_arr = []
- for msg in messages:
- msg_arr.append({"t": str(msg['type']), "s": str(msg['from']), "r": str(msg['to']), "m": str(msg['message'])})
-
- return msg_arr
-
- def _sanitize(self, message):
- return message.replace('>', '>').replace('<', '<').replace('&', '&');
-
-
diff --git a/addons/web_chat/i18n/es.po b/addons/web_chat/i18n/es.po
deleted file mode 100644
index 67063226b21..00000000000
--- a/addons/web_chat/i18n/es.po
+++ /dev/null
@@ -1,20 +0,0 @@
-# Spanish translation for openerp-web
-# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
-# This file is distributed under the same license as the openerp-web package.
-# FIRST AUTHOR , 2012.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: openerp-web\n"
-"Report-Msgid-Bugs-To: FULL NAME \n"
-"POT-Creation-Date: 2012-02-06 17:33+0100\n"
-"PO-Revision-Date: 2012-02-08 02:04+0000\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: Spanish \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-02-09 07:27+0000\n"
-"X-Generator: Launchpad (build 14763)\n"
-
-
diff --git a/addons/web_chat/i18n/es_CR.po b/addons/web_chat/i18n/es_CR.po
deleted file mode 100644
index 238f3396c1a..00000000000
--- a/addons/web_chat/i18n/es_CR.po
+++ /dev/null
@@ -1,20 +0,0 @@
-# Spanish translation for openerp-web
-# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
-# This file is distributed under the same license as the openerp-web package.
-# FIRST AUTHOR , 2012.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: openerp-web\n"
-"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2012-02-06 17:33+0100\n"
-"PO-Revision-Date: 2012-02-08 02:26-0600\n"
-"Last-Translator: Carlos Vásquez - CLEARCORP \n"
-"Language-Team: Spanish \n"
-"Language: es\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-02-08 06:38+0000\n"
-"X-Generator: Launchpad (build 14747)\n"
-
diff --git a/addons/web_chat/i18n/web_chat.pot b/addons/web_chat/i18n/web_chat.pot
deleted file mode 100644
index fa7bd80d130..00000000000
--- a/addons/web_chat/i18n/web_chat.pot
+++ /dev/null
@@ -1,19 +0,0 @@
-# Translations template for PROJECT.
-# Copyright (C) 2012 ORGANIZATION
-# This file is distributed under the same license as the PROJECT project.
-# FIRST AUTHOR , 2012.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: PROJECT VERSION\n"
-"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2012-02-06 17:33+0100\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: LANGUAGE \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Generated-By: Babel 0.9.6\n"
-
diff --git a/addons/web_chat/static/lib/AjaxIM/LICENSE.txt b/addons/web_chat/static/lib/AjaxIM/LICENSE.txt
deleted file mode 100644
index 78685f1b2b8..00000000000
--- a/addons/web_chat/static/lib/AjaxIM/LICENSE.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright (c) 2005 - 2010 Joshua Gross, http://ajaxim.com
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
\ No newline at end of file
diff --git a/addons/web_chat/static/lib/AjaxIM/js/im.js b/addons/web_chat/static/lib/AjaxIM/js/im.js
deleted file mode 100644
index 9ab195fd6b6..00000000000
--- a/addons/web_chat/static/lib/AjaxIM/js/im.js
+++ /dev/null
@@ -1,2101 +0,0 @@
-// = im.js =
-//
-// **Copyright © 2005 – 2010 Joshua Gross**\\
-// //MIT Licensed//
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-// This is the main library for Ajax IM. It encompasses the UI controls,
-// and connecting with the server. It does //not// handle registration or
-// account management.
-
-(function($, _instance) {
- AjaxIM = function(options, actions) {
- if(this instanceof AjaxIM) {
- var self = this;
-
- // === {{{ defaults }}} ===
- //
- // These are the available settings for Ajax IM, and the associated
- // defaults:
- //
- // * {{{pollType}}} determines the way in which the client will talk to
- // the server.
- // ** {{{comet}}} will use http streaming, wherein a connection to the server will be held open indefinitely, and the server will push down new events (messages, status updates) as they occur.
- // ** {{{long}}} will hold open a connection with the server for as long as possible, or until a new event is received. Upon the server sending an event or closing the connection, the client will automatically—and immediately—reconnect.
- // ** {{{short}}} will open a connection, and the server (if this method is supported) will //immediately// provide a response as to whether or not there are any new events. Once a response is received, the client will wait 5 seconds, and then reconnect.
- // * {{{pollServer}}} is the default URL to which all actions refer. It is
- // possible to specify certain action URLs separately (as is used with the
- // NodeJS server).
- // * {{{cookieName}}} is the name of the session cookie used by the server.
- // If this is not set properly, the IM engine will not be able to automatically
- // reinitialize sessions.
- // * {{{theme}}} is the name of the theme folder that defines the HTML and
- // CSS of the IM bar and chat boxes. Usually, themes are deposited in the
- // provided "themes" folder and specified by that path, e.g. {{{themes/default}}}.
- // Theme files within the theme folder must be named {{{theme.html}}} and
- // {{{theme.css}}}.
- // * {{{storageMethod}}} defines the way in which data (chat sessions) are
- // temporarily stored client-side. By default, {{{"flash"}}} is used because
- // it is the most widely supported method. However,
- // [[http://eric.garside.name/docs.html?p=jstore#js-engines|other storage engines]]
- // are available, with their respective up- and down-sides
- // outlined, on the jStore website.
- // * {{{storeSession}}} (**not implemented**) sets the number of days to
- // retain stored chat session data before it should be deleted.
- // * {{{checkResume}}} is a flag that sets whether or not the client should
- // make a call to the server before resuming the session (such as on a page
- // reload). This will ensure that the session has not expired. If set to {{{false}}},
- // a call to the server will not be made, and the session will be assumed to
- // be active.
- var defaults = {
- pollType: 'long',
- pollServer: './ajaxim.php',
- cookieName: 'ajaxim_session',
- theme: 'themes/default',
- storageMethod: 'auto',
- flashStorage: 'js/jStore.Flash.html',
- storeSession: 5, // number of days to store chat data (0 for current session only)
- checkResume: true
- };
-
- // === {{{AjaxIM.}}}**{{{settings}}}** ===
- //
- // These are the settings for the IM. If particular options are not specified,
- // the defaults (see above) will be used. //These options will be defined
- // upon calling the initialization function, and not set directly.//
- this.settings = $.extend(defaults, options);
-
- // === {{{AjaxIM.}}}**{{{actions}}}** ===
- //
- // Each individual action that the IM engine can execute is predefined here.
- // By default, it merely appends the action onto the end of the {{{pollServer}}} url,
- // however, it is possible to define actions individually. //The alternative actions
- // will be defined upon calling the initialization function, and not set directly.//
- //
- // Should you define an action at a different URL, Ajax IM will determine whether
- // or not this URL is within the current domain. If it is within a subdomain of
- // the current domain, it will set the document.domain variable for you,
- // to match a broader hostname scope; the action will continue to use {{{$.post}}}
- // (the default AJAX method for Ajax IM).
- //
- // On the other hand, should you choose a URL outside the current domain
- // Ajax IM will switch to {{{$.getJSON}}} (a get request) to avoid
- // cross-domain scripting issues. This means that a server on a different
- // port or at a different address will need to be able to handle GET
- // requests rather than POST requests (such as how the Node.JS Ajax IM
- // server works).
- this.actions = $.extend({
- login: this.settings.pollServer + '/login',
- logout: this.settings.pollServer + '/logout',
- register: this.settings.pollServer + '/register',
- poll: this.settings.pollServer + '/poll',
- send: this.settings.pollServer + '/send',
- status: this.settings.pollServer + '/status',
- resume: this.settings.pollServer + '/resume'
- }, actions);
-
- var httpRx = new RegExp('^http://', 'i'),
- slashslashRx = new RegExp('^//', 'i'),
- queryStrRx = new RegExp('[?](.+)$'),
- subdomainRx = new RegExp('((http[s]?:)?//)?(.+?)[.]' + window.location.host, 'i');
- $.each(this.actions, function(name, action) {
- if(name == 'poll' || name == 'send') {
- if(self.settings.pollType != 'comet')
- action += (queryStrRx.test(action[1]) ? '&' : '?') + 'callback=?';
-
- action = ['jsonp', action];
- } else {
- action = ['ajax', action];
- if(subdomainRx.test(action[1])) {
- document.domain = '.' + window.location.host;
- } else if((http = httpRx.test(action[1])) || slashslashRx.test(action[1])) {
- if(!(new RegExp('//' + window.location.host + '/', 'i')).test(action[1])) {
- action[1] += (queryStrRx.test(action[1]) ? '&' : '?') + 'callback=?';
- action = ['jsonp', action[1]];
- }
- }
- }
-
- self.actions[name] = action;
- });
-
- // We load the theme dynamically based on the passed
- // settings. If the theme is set to false, we assume
- // that the user is going to load it himself.
- this.themeLoaded = false;
- if(this.settings.theme) {
- $('').appendTo('body').load(this.settings.theme + '/theme.html #imjs-bar, .imjs-tooltip',
- function() {
- self.themeLoaded = true;
- setup.apply(self);
- }
- );
- if(typeof document.createStyleSheet == 'function')
- document.createStyleSheet(this.settings.theme + '/theme.css');
- else
- $('body').append('');
- } else {
- this.themeLoaded = true;
- setup.apply(this);
- }
-
- // Client-side storage for keeping track of
- // conversation states, active tabs, etc.
- // The default is flash storage, however, other
- // options are available via the jStore library.
- if(this.settings.storageMethod) {
- this.storageBrowserKey = 'unknown';
- this.storeKey = '';
-
- $.each($.browser, function(k, v) {
- if(k == 'version') return true;
- else if(v == true) {
- self.storageBrowserKey = k;
- return false;
- }
- });
-
- if(this.settings.storageMethod == 'auto') {
- $.each(['ie', 'html5', 'local', 'flash'], function() {
- if($.jStore.Availability[this]()) {
- self.settings.storageMethod = this;
- return false;
- }
- });
- }
-
- $.extend($.jStore.defaults, {
- project: 'im.js',
- engine: this.settings.storageMethod,
- flash: this.settings.flashStorage
- });
-
- this.storageReady = false;
-
- if(this.settings.storageMethod == 'flash') {
- $.jStore.ready(function(engine) {
- $.jStore.flashReady(function() {
- self.storageReady = true;
- setup.apply(self);
- });
- })
- } else {
- $.jStore.ready(function(engine) {
- self.storageReady = true;
- setup.apply(self);
- });
- }
-
- $.jStore.load();
- } else {
- this.storageReady = true;
- setup.apply(this);
- }
-
- // Allow a chatbox to me minimized
- $('.imjs-chatbox').live('click', function(e) {
- e.preventDefault();
- return false;
- });
-
- $('.imjs-chatbox .imjs-minimize').live('click', function() {
- $(this).parents('.imjs-chatbox').data('tab').click();
- });
-
- // Allow a chatbox to be closed
- $('.imjs-chatbox .imjs-close').live('click', function() {
- var chatbox = $(this).parents('.imjs-chatbox');
- chatbox.data('tab')
- .data('state', 'closed').css('display', 'none');
-
- if(self.settings.storageMethod && self.storageReady) {
- delete self.chatstore[chatbox.data('username')];
- if(self.storageReady) {
- $.jStore.store(
- self.storageBrowserKey + '-' +
- self.username + '-chats',
- self.chatstore);
- }
- }
- });
-
- // Setup message sending for all chatboxes
- $('.imjs-chatbox .imjs-input').live('keydown', function(event) {
- var obj = $(this);
- if(event.keyCode == 13 && !($.browser.msie && $.browser.version < 8)) {
- self.send(obj.parents('.imjs-chatbox').data('username'), obj.val());
- }
- }).live('keyup', function(event) {
- if(event.keyCode == 13) {
- if($.browser.msie && $.browser.version < 8) {
- var obj = $(this);
- self.send(obj.parents('.imjs-chatbox').data('username'), obj.val());
- }
-
- var obj = $(this);
- obj.val('');
- obj.height(obj.data('height'));
- }
- }).live('keypress', function(e) {
- var obj = $(this);
- if(!($.browser.msie && $.browser.opera)) obj.height(0);
- if(this.scrollHeight > obj.height() || this.scrollHeight < obj.height()) {
- obj.height(this.scrollHeight);
- }
- });
-
- $('.imjs-msglog').live('click', function() {
- var chatbox = $(this).parents('.imjs-chatbox');
- chatbox.find('.imjs-input').focus();
- });
-
- // Create a chatbox when a buddylist item is clicked
- $('.imjs-friend').live('click', function() {
- var chatbox = self._createChatbox($(this).data('friend'));
-
- if(chatbox.data('tab').data('state') != 'active')
- chatbox.data('tab').click();
-
- chatbox.find('.imjs-input').focus();
- });
-
- // Setup and hide the scrollers
- $('.imjs-scroll').css('display', 'none');
- $('#imjs-scroll-left').live('click', function() {
- var hiddenTab = $('#imjs-bar li.imjs-tab:visible').slice(-1)
- .next('#imjs-bar li.imjs-tab:hidden')
- .filter(function() {
- return $(this).data('state') != 'closed'
- })
- .not('.imjs-default').slice(-1).css('display', '');
-
- if(hiddenTab.length) {
- $('#imjs-bar li.imjs-tab:visible').eq(0).css('display', 'none');
- $(this).html(parseInt($(this).html()) - 1);
- $('#imjs-scroll-right').html(parseInt($('#imjs-scroll-right').html()) + 1);
- }
-
- return false;
- });
- $('#imjs-scroll-right').live('click', function() {
- var hiddenTab = $('#imjs-bar li.imjs-tab:visible').eq(0)
- .prev('#imjs-bar li.imjs-tab:hidden')
- .filter(function() {
- return $(this).data('state') != 'closed'
- })
- .not('.imjs-default').slice(-1).css('display', '');
-
- if(hiddenTab.length) {
- $('#imjs-bar li.imjs-tab:visible').slice(-1).css('display', 'none');
- $(this).html(parseInt($(this).html()) - 1);
- $('#imjs-scroll-left').html(parseInt($('#imjs-scroll-left').html()) + 1);
- }
-
- return false;
- });
-
- // Initialize the chatbox hash
- this.chats = {};
-
- // Try to resume any existing session
- this.resume();
-
- $(window).resize(function() {
- self.bar._scrollers();
- });
- } else
- return AjaxIM.init(options);
- };
-
- // We predefine all public functions here...
- // If they are called before everything (theme, storage engine) has loaded,
- // then they get put into a "prequeue" and run when everything *does*
- // finally load.
- //
- // This ensures that nothing loads without all of the principal components
- // being pre-loaded. If that were to occur (without this prequeue), things
- // would surely break.
- var prequeue = [];
- var empty = function() {
- var func = this;
- return function() { prequeue.push([func, arguments]) };
- };
- $.extend(AjaxIM.prototype, {
- settings: {},
- friends: {},
- chats: {},
-
- storage: empty.apply('storage'),
- login: empty.apply('login'),
- logout: empty.apply('logout'),
- resume: empty.apply('resume'),
- form: empty.apply('form'),
- poll: empty.apply('poll'),
- incoming: empty.apply('incoming'),
- send: empty.apply('send'),
- status: empty.apply('status'),
- statuses: {},
- bar: {
- initialize: empty.apply('bar.initialize'),
- activateTab: empty.apply('bar.activateTab'),
- closeTab: empty.apply('bar.closeTab'),
- addTab: empty.apply('bar.addTab'),
- notification: empty.apply('bar.notification'),
- _scrollers: empty.apply('bar._scrollers')
- }
- });
-
- setup = (function() {
- var self = this;
- if(!this.storageReady || !this.themeLoaded) return;
-
- $(self).trigger('loadComplete');
- $(window).resize();
-
- $.extend(AjaxIM.prototype, {
- // == Cookies ==
- //
- // The "cookies" functions can be used to set, get, and erase JSON-based cookies.
- // These functions are primarily used to manage and read the server-set cookie
- // that handles the user's chat session ID.
- cookies: {
- // === {{{AjaxIM.}}}**{{{cookies.set(name, value, days)}}}** ===
- //
- // Sets a cookie, stringifying the JSON value upon storing it.
- //
- // ==== Parameters ====
- // * {{{name}}} is the cookie name.\\
- // * {{{value}}} is the cookie data that you would like to store.\\
- // * {{{days}}} is the number of days that the cookie will be stored for.
- set: function(name, value, days) {
- if (days) {
- var date = new Date();
- date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
- var expires = "; expires=" + date.toGMTString();
- } else var expires = "";
- document.cookie = name + "=" + $.compactJSON(value) + expires + "; path=/";
- },
-
- // === {{{AjaxIM.}}}**{{{cookies.get(name)}}}** ===
- //
- // Gets a cookie, decoding the JSON value before returning the data.
- //
- // ==== Parameters ====
- // * {{{name}}} is the cookie name that you would like to retrieve.
- get: function(name) {
- var nameEQ = name + "=";
- var ca = document.cookie.split(';');
- for(var i = 0; i < ca.length; i++) {
- var c = ca[i];
- while (c.charAt(0) == ' ') c = c.substring(1, c.length);
- if (c.indexOf(nameEQ) == 0) {
- var cval = decodeURIComponent(c.substring(nameEQ.length, c.length));
- return $.secureEvalJSON(cval);
- }
- }
- return null;
- },
-
- // === {{{AjaxIM.}}}**{{{cookies.erase(name)}}}** ===
- //
- // Deletes a cookie.
- //
- // {{{name}}} is the existing cookie that you would like to delete.
- erase: function(name) {
- self.cookies.set(name, '', -1);
- }
- },
-
- // == Main ==
-
- // === {{{AjaxIM.}}}**{{{storage()}}}** ===
- //
- // Retrieves chat session data from whatever storage engine is enabled
- // (provided that one is enabled at all). If a page reloads, this function
- // is called to restore the user's chat state (existing conversations, active tab).
- // This function is called //automatically//, upon initialization of the IM engine.
- storage: function() {
- if(!self.storeKey.length) return;
-
- try {
- var chatstore = $.jStore.store(self.storeKey + 'chats') || {};
- } catch(e) {
- $.jStore.remove(self.storeKey + 'chats');
- var chatstore = {};
- }
-
- if(this.chatstore) {
- $.each(this.chatstore, function(username, convo) {
- if(username in chatstore)
- chatstore[username] = $.merge(chatstore[username], self.chatstore[username]);
- else
- chatstore[username] = self.chatstore[username];
- });
-
- this.chatstore = chatstore;
- $.jStore.store(self.storeKey + 'chats', chatstore);
- } else {
- this.chatstore = chatstore;
- }
-
- $.each(this.chatstore, function(username, convo) {
- if(!convo.length) return;
-
- var chatbox = self._createChatbox(username, true);
- chatbox.data('lastDateStamp', null).css('display', 'none');
-
- // Remove the automatic date stamp
- chatbox.find('.imjs-msglog').empty();
-
- // Restore all messages, date stamps, and errors
- $.each(convo, function() {
- switch(this[0]) {
- case 'error':
- self._addError(chatbox, decodeURIComponent(this[2]), this[3]);
- break;
-
- case 'datestamp':
- self._addDateStamp(chatbox, this[3]);
- break;
-
- case 'a':
- case 'b':
- self._addMessage(this[0], chatbox, this[1],
- decodeURIComponent(this[2]), this[3]);
- break;
- }
- });
-
- $(self).trigger('chatRestored', [username, chatbox]);
- });
-
- var activeTab = $.jStore.store(self.storeKey + 'activeTab') || [];
- if(activeTab.length && (activeTab = activeTab[0]) && activeTab in this.chats) {
- this.chats[activeTab].data('tab').click();
- var msglog = this.chats[activeTab].find('.imjs-msglog');
- msglog[0].scrollTop = msglog[0].scrollHeight;
- }
- },
-
- // === {{{AjaxIM.}}}**{{{login(username, password)}}}** ===
- //
- // Authenticates a user and initializes the IM engine. If the user is
- // already logged in, [s]he is logged out, the session is cleared, and
- // the new user is logged in.
- //
- // Returns the user's properly formatted username, session ID, and online
- // friends in JSON, if successful; e.g.:\\
- // {{{ {u: 'username', s: 'longsessionid', f: [{u: 'friend', s: 1, g: 'group'}]} }}}
- //
- // If unsuccessful, {{{false}}} is returned.
- //
- // ==== Parameters ====
- // * {{{username}}} is the user's username.\\
- // * {{{password}}} is the user's password. This password will be MD5 hashed
- // before it is sent to the server.
- login: function(username, password) {
- if(!username) username = '';
- if(!password) password = '';
-
- if(this.username)
- return true; // Already logged in!
-
- // hash password before sending it to the server
- password = $.md5(password);
-
- // authenticate
- AjaxIM.request(
- this.actions.login,
- {'username': username, 'password': password},
- function(auth) {
- if(auth.r == 'logged in') {
- self.username = ('u' in auth ? auth.u : username);
-
- if(self.settings.storageMethod)
- self.storeKey = [self.storageBrowserKey, self.username, ''].join('-');
-
- var existing = self.cookies.get(self.settings.cookieName);
- self.storage();
-
- // Begin the session
- $.each(auth.f, function() {
- self.friends[this.u] = {status: [this.s, ''], group: this.g};
- });
- self._session(self.friends);
- self._storeFriends();
-
- $(self).trigger('loginSuccessful', [auth]);
-
- return auth;
- } else {
- $(self).trigger('loginError', [auth]);
- }
-
- return false;
- }
- );
- },
-
- // === {{{AjaxIM.}}}**{{{logout()}}}** ===
- //
- // Logs the user out and removes his/her session cookie. As well, it
- // will close all existing chat windows, clear the storage engine, and
- // remove the IM bar.
- logout: function() {
- AjaxIM.request(
- this.actions.logout,
- {},
- function(done) {
- if(done) {
- self.cookies.erase(self.settings.cookieName);
- self._clearSession();
- $(self).trigger('logoutSuccessful');
- return true;
- } else {
- $(self).trigger('logoutError');
- return false;
- }
- }
- );
- },
-
- // === {{{AjaxIM.}}}**{{{resume()}}}** ===
- //
- // Resumes an existing session based on a session ID stored in the
- // server-set cookie. This function is called //automatically// upon IM
- // engine (re-)initialization, so the user does not need to re-login
- // should a session already exist.
- resume: function() {
- var session = this.cookies.get(this.settings.cookieName);
-
- if(session && session.sid) {
- this.username = session.user;
-
- this.storeKey = [this.storageBrowserKey, this.username, ''].join('-');
-
- var friends = $.jStore.store(this.storeKey + 'friends') || [];
- if(self.settings.checkResume) {
- AjaxIM.request(
- this.actions.resume,
- {},
- function(response) {
- if(response.r == 'connected') {
- self._session(friends);
- self.storage();
- } else {
- var username = this.username;
- self._clearSession();
- $(self).trigger('sessionNotResumed', [username]);
- }
- }
- );
- } else {
- self._session(friends);
- self.storage();
- }
- } else {
- $(self).trigger('noSession');
- }
- },
-
- // === //private// {{{AjaxIM.}}}**{{{_session(friends)}}}** ===
- //
- // Restores session data (username, friends) and begins polling the server.
- // Called only by {{{AjaxIM.resume()}}}.
- //
- // ==== Parameters ====
- // * {{{friends}}} is a list of "friend" objects, e.g.:\\
- // {{{[{u: 'friend', s: 1, g: 'group'}, ...]}}}
- // ** {{{u}}} being the friend's username.
- // ** {{{s}}} being one of the available status codes (see {{{AjaxIM.statuses}}}), depending on the friend's current status.
- // ** {{{g}}} being the group that the friend is in.
- _session: function(friends) {
- $('#imjs-friends-panel .imjs-header span').html(this.username);
- $('#imjs-friends').removeClass('imjs-not-connected');
-
- $.each(friends, function(friend, info) {
- self.addFriend.apply(self, [friend, info.status, info.group]);
- });
- self._storeFriends();
-
- $(self).trigger('sessionResumed', [this.username]);
-
- setTimeout(function() { self.poll(); }, 0);
- },
-
- // === //private// {{{AjaxIM.}}}**{{{_clearSession()}}}** ===
- //
- // Clears all session data from the last known user.
- _clearSession: function() {
- if(self.settings.storageMethod && self.storageReady) {
- $.jStore.remove(self.storeKey + 'chats');
- $.jStore.remove(self.storeKey + 'friends');
- $.jStore.remove(self.storeKey + 'activeTab');
- }
-
- self.chats = {};
- $('.imjs-tab').not('.imjs-tab.imjs-default').remove();
-
- self.cookies.erase('ajaxim_session');
- delete self.username;
- },
-
- // === {{{AjaxIM.}}}**{{{form(element)}}}** ===
- //
- // Loads a login and registration form into the specified element
- // or, if no element is supplied, to the location on the page from
- // which this function was called.
- form: function(element) {
- $(element).load(this.settings.theme + '/theme.html #imjs-lr', function() {
- $('#imjs-lr .error').hide();
-
- if(self.username) {
- $('#imjs-register, #imjs-login fieldset').hide();
- $('#imjs-logged-in')
- .show()
- .html($('#imjs-logged-in').html().replace('{username}', self.username));
- } else {
- $('#imjs-logged-in').hide();
- }
-
- // Handle logout success
- $(self).bind('logoutSuccessful', function() {
- $('#imjs-login fieldset').slideDown();
- $('#imjs-register').slideDown();
-
- $('#imjs-logged-in')
- .html($('#imjs-logged-in strong').html('{username}'))
- .slideUp();
- });
- $('#imjs-logged-in a').click(function() {
- self.logout();
- return false;
- });
-
- // Handle login error or success
- $(self).bind('loginError', function() {
- $('#imjs-login .error').html(AjaxIM.i18n.authInvalid).slideDown('fast');
- $('#imjs-login input')
- .addClass('imjs-lr-error')
- .blur(function() { $(this).removeClass('imjs-lr-error'); });
- }).bind('loginSuccessful', function() {
- $('#imjs-login fieldset').slideUp();
- $('#imjs-register').slideUp();
-
- $('#imjs-logged-in')
- .html($('#imjs-logged-in').html().replace('{username}', self.username))
- .slideDown();
- });
- var login = function() {
- self.login($('#imjs-login-username').val(), $('#imjs-login-password').val());
- return false;
- };
- $('#imjs-login').submit(login);
- $('#imjs-login-submit').click(login);
-
- var regIssues = false;
- var regError = function(error, fields) {
- $('#imjs-register .error')
- .append(AjaxIM.i18n['register' + error] + ' ')
- .slideDown();
- $(fields)
- .addClass('imjs-lr-error')
- .blur(function() { $(this).removeClass('imjs-lr-error'); });
- regIssues = true;
- };
-
- var register = function() {
- $('#imjs-register .error').empty();
-
- regIssues = false;
-
- var username = $('#imjs-register-username').val();
- var password = $('#imjs-register-password').val();
-
- if(password.length < 4)
- regError('PasswordLength', '#imjs-register-password');
-
- if(password != $('#imjs-register-cpassword').val())
- regError('PasswordMatch', '#imjs-register-password, #imjs-register-cpassword');
-
- if(username.length <= 2 ||
- !$('#imjs-register-username').val().match(/^[A-Za-z0-9_.]+$/))
- regError('UsernameLength', '#imjs-register-username');
-
- if(!regIssues) {
- AjaxIM.request(
- self.actions.register,
- {username: username, password: password},
- function(response) {
- if(response.r == 'registered') {
- self.login(username, password);
- } else if(response.r == 'error') {
- switch(response.e) {
- case 'unknown':
- regError('Unknown', '');
- break;
-
- case 'invalid password':
- regError('PasswordLength', '#imjs-register-password');
- break;
-
- case 'invalid username':
- regError('UsernameLength', '#imjs-register-username');
- break;
-
- case 'username taken':
- regError('UsernameTaken', '#imjs-register-username');
- break;
- }
- }
- },
- function(error) {
- regError('Unknown', '');
- }
- );
- }
-
- return false;
- };
- $('#imjs-register').submit(register);
- $('#imjs-register-submit').click(register);
- });
- },
-
- // === {{{AjaxIM.}}}**{{{poll()}}}** ===
- //
- // Queries the server for new messages. If a 'long' or 'short' poll
- // type is used, jQuery's {{{$.post}}} or {{{$.getJSON}}} will be
- // used. If 'comet' is used, the server connection will be deferred
- // to the comet set of functions.
- poll: function() {
- if(/^(short|long)$/.test(this.settings.pollType)) {
- AjaxIM.request(
- this.actions.poll,
- {},
- function(response) {
- if(!response['e']) {
- if(response.length)
- self._parseMessages(response);
-
- setTimeout(function() { self.poll(); }, 0);
- } else {
- switch(response.e) {
- case 'no session found':
- self._notConnected();
- break;
- }
-
- $(self).trigger('pollFailed', [response.e]);
- }
- },
- function(error) {
- self._notConnected();
- $(self).trigger('pollFailed', ['not connected']);
- // try reconnecting?
- }
- );
- } else if(this.settings.pollType == 'comet') {
- this.comet.connect();
- }
- },
-
- // === //private// {{{AjaxIM.}}}**{{{_parseMessages(messages)}}}** ===
- //
- // Handles an incoming message array:\\
- // {{{[{t: 'type', s: 'sender', r: 'recipient', m: 'message'}, ...]}}}
- //
- // * {{{t}}} (message type) is one of:
- // ** {{{m}}} — a standard message
- // ** {{{s}}} — a user's status update
- // ** {{{b}}} — a broadcasted message (sent to many users simultaneously)
- // * {{{s}}} is the sender of the message.
- // * {{{r}}} is the intended recipient of the message. Most of the time, this will
- // simply be the logged in user, however, a broadcasted message may not specify
- // a recipient or may specify a different recipient. Also provides future
- // compatability for chatrooms.
- // * {{{m}}} is the actual message. For something such as a status update, this can
- // be a JSON object or parsable string; e.g. {{{"2:I'm away."}}}
- //
- // ==== Parameters ====
- // * {{{messages}}} is the message array
- _parseMessages: function(messages) {
- if($.isArray(messages)) {
- $.each(messages, function() {
- $(self).trigger('parseMessage', [this]);
-
- switch(this.t) {
- case 'm':
- self.incoming(this.s, this.m);
- break;
-
- case 's':
- var status = this.m.split(':');
- if(this['g'])
- self.addFriend(this.s, status, this.g);
- self._friendUpdate(this.s, status[0], status.slice(1).join(':'));
- self._storeFriends();
- break;
-
- case 'b':
- break;
-
- default:
- break;
- }
- });
- }
- },
-
- // === {{{AjaxIM.}}}**{{{incoming(from, message)}}}** ===
- //
- // Handles a new message from another user. If a chatbox for that
- // user does not yet exist, one is created. If it does exist, but
- // is minimized, the user is notified but the chatbox is not brought
- // to the front. This function also stores the message, if a storage
- // method is set.
- //
- // ==== Parameters ====
- // * {{{from}}} is the username of the sender.
- // * {{{message}}} is the body.
- incoming: function(from, message) {
- // check if IM exists, otherwise create new window
- // TODO: If friend is not on the buddylist,
- // should add them to a temp list?
- var chatbox = this._createChatbox(from);
-
- if(!$('#imjs-bar .imjs-selected').length) {
- chatbox.data('tab').click();
- } else if(chatbox.data('tab').data('state') != 'active') {
- this.bar.notification(chatbox.data('tab'));
- }
-
- var time = this._addMessage('b', chatbox, from, message);
- this._storeMessage('b', chatbox, from, message, time);
- },
-
- // === {{{AjaxIM.}}}**{{{addFriend(username, group)}}}** ===
- //
- // Inserts a new friend into the friends list. If the group specified
- // doesn't exist, it is created. If the friend is already in this group,
- // they aren't added again, however, the friend item is returned.
- //
- // ==== Parameters ====
- // * {{{username}}} is the username of the new friend.
- // * {{{status}}} is the current status of the friend.
- // * {{{group}}} is the user group to which the friend should be added.
- addFriend: function(username, status, group) {
- var status_name = 'available';
- $.each(this.statuses,
- function(key, val) { if(status[0] == val) { status_name = key; return false; } });
-
- var group_id = 'imjs-group-' + $.md5(group);
-
- if(!(group_item = $('#' + group_id)).length) {
- var group_item = $('.imjs-friend-group.imjs-default').clone()
- .removeClass('imjs-default')
- .attr('id', group_id)
- .data('group', group)
- .appendTo('#imjs-friends-list');
-
- var group_header = group_item.find('.imjs-friend-group-header');
- group_header.html(group_header.html().replace('{group}', group));
- }
-
- var user_id = 'imjs-friend-' + $.md5(username + group);
-
- if(!$('#' + user_id).length) {
- var user_item = group_item.find('ul li.imjs-default').clone()
- .removeClass('imjs-default')
- .addClass('imjs-' + status_name)
- .attr('id', user_id)
- .data('friend', username)
- .appendTo(group_item.find('ul'));
- if(status[0] == 0) user_item.hide();
- user_item.html(user_item.html().replace('{username}', username));
- }
-
- this.friends[username] = {'status': status, group: group};
-
- this._updateFriendCount();
-
- return this.friends[username];
- },
-
- // === //private// {{{AjaxIM.}}}**{{{_updateFriendCount()}}}** ===
- //
- // Counts the number of online friends and updates the friends count
- // in the friend tab.
- _updateFriendCount: function() {
- var friendsLength = 0;
- for(var f in this.friends) {
- if(this.friends[f].status[0] != 0)
- friendsLength++;
- }
- $('#imjs-friends .imjs-tab-text span span').html(friendsLength);
- },
-
- // === //private// {{{AjaxIM.}}}**{{{_storeFriends()}}}** ===
- //
- // If a storage method is enabled, the current state of the
- // user's friends list is stored.
- _storeFriends: function() {
- if(this.settings.storageMethod && this.storageReady)
- $.jStore.store(this.storeKey + 'friends', this.friends);
- },
-
- // === //private// {{{AjaxIM.}}}**{{{_createChatbox(username)}}}** ===
- //
- // Builds a chatbox based on the default chatbox HTML and CSS defined
- // in the current theme. Should a chatbox for this user already exist,
- // a new one is not created. Instead, it is either given focus (should
- // no other windows already have focus), or a notification is issued.
- //
- // As well, if the chatbox does not exist, an associated tab will be
- // created.
- //
- // ==== Parameters ====
- // * {{{username}}} is the name of the user for whom the chatbox is intended
- // for.
- // * {{{no_stamp}}} sets whther or not to add a date stamp to the chatbox
- // upon creation.
- //
- // //Note:// New chatboxes are given an automatically generated ID in the
- // format of {{{#imjs-[md5 of username]}}}.
- _createChatbox: function(username, no_stamp) {
- var chatbox_id = 'imjs-' + $.md5(username);
- if(!(chatbox = $('#' + chatbox_id)).size()) {
- // add a tab
- var tab = this.bar.addTab(username, '#' + chatbox_id);
- var chatbox = tab.find('.imjs-chatbox');
- chatbox.attr('id', chatbox_id);
-
- chatbox.data('tab', tab);
-
- // remove default items from the message log
- var message_log = chatbox.find('.imjs-msglog').empty();
-
- // setup the chatbox header
- var cb_header = chatbox.find('.imjs-header');
- cb_header.html(cb_header.html().replace('{username}', username));
-
- if(!no_stamp) {
- // add a date stamp
- var time = this._addDateStamp(chatbox);
- this._storeNonMessage('datestamp', username, null, time);
- }
-
- // associate the username with the object and vice-versa
- this.chats[username] = chatbox;
- chatbox.data('username', username);
-
- // did this chatbox fall down?
- this.bar._scrollers();
-
- if(username in this.friends) {
- status = this.friends[username].status;
- var status_name = 'available';
- $.each(this.statuses,
- function(key, val) { if(status[0] == val) { status_name = key; return false; } });
- tab.addClass('imjs-' + status_name);
- }
-
- // store inputbox height
- //var input = chatbox.find('.imjs-input');
- //input.data('height', input.height());
- } else if(chatbox.data('tab').data('state') == 'closed') {
- chatbox.find('.imjs-msglog > *').addClass('imjs-msg-old');
-
- var tab = chatbox.data('tab');
- if(tab.css('display') == 'none')
- tab.css('display', '').removeClass('imjs-selected')
- .appendTo('#imjs-bar');
-
- if(!no_stamp) {
- // possibly add a date stamp
- var time = this._addDateStamp(chatbox);
- this._storeNonMessage('datestamp', username, null, time);
- }
-
- if(!$('#imjs-bar .imjs-selected').length) {
- tab.click();
- } else {
- this.bar.notification(tab);
- }
- }
-
- return chatbox;
- },
-
- // === //private// {{{AjaxIM.}}}**{{{_addDateStamp(chatbox)}}}** //
- //
- // Adds a date/time notifier to a chatbox. These are generally
- // inserted upon creation of a chatbox, or upon the date changing
- // since the last time a date stamp was added. If a date stamp for
- // the current date already exists, a new one will not be added.
- //
- // ==== Parameters ====
- // * {{{chatbox}}} refers to the jQuery-selected chatbox DOM element.
- // * {{{time}}} is the date/time the date stamp will show. It is specified
- // in milliseconds since the Unix Epoch. This is //only// defined when
- // date stamps are being restored from storage; if not specified, the
- // current computer time will be used.
- _addDateStamp: function(chatbox, time) {
- var message_log = $(chatbox).find('.imjs-msglog');
- if(!time)
- time = (new Date()).getTime();
-
- 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(AjaxIM.dateFormat(time, date_stamp_time.html()));
-
- var date_stamp_date = date_stamp.find('.imjs-date-date');
- var formatted_date = AjaxIM.dateFormat(time, date_stamp_date.html());
- if(chatbox.data('lastDateStamp') != formatted_date) {
- if(date_stamp_date.length)
- date_stamp_date.html(AjaxIM.dateFormat(time, date_stamp_date.html()));
-
- chatbox.data('lastDateStamp', formatted_date);
- date_stamp.appendTo(message_log);
- } else {
- //$('').appendTo(message_log);
- }
-
- return time;
- },
-
- // === //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(AjaxIM.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;
- },
-
- // === //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.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 = AjaxIM.dateFormat(time, msgtime.html());
-
- msgtime.html(time);
-
- var msglog = chatbox.find('.imjs-msglog');
- msglog[0].scrollTop = msglog[0].scrollHeight;
-
- return time;
- },
-
- // === //private// {{{AjaxIM.}}}**{{{_storeNonMessage(type, username, data, time)}}}** ===
- //
- // **Redundant?**\\
- // Similar to {{{AjaxIM._storeMessage}}}, but stores items that aren't messages,
- // such as datestamps and errors.
- //
- // ==== Parameters ====
- // * {{{type}}} is the type of non-message (error, datestamp).
- // * {{{username}}} is the username of the user being chatted with.
- // * {{{data}}} is the (optional) data of the non-message to be stored.
- // * {{{time}}} is the time of the message.
- _storeNonMessage: function(type, username, data, time) {
- // If storage is on & ready, store the non-message
- if(this.settings.storageMethod) {
- if(!this.chatstore) this.chatstore = {};
- if(!(username in this.chatstore)) this.chatstore[username] = [];
-
- // If the chat store gets too long, it becomes slow to load.
- if(this.chatstore[username].length > 300)
- this.chatstore[username].shift();
-
- // For some reason, if we don't encode and decode the message, it *will* break
- // (at least) the Flash storage engine's retrieval. Gah!
- this.chatstore[username].push(
- [type, username, encodeURIComponent(data), time]);
-
- if(this.storageReady) $.jStore.store(self.storeKey + 'chats', this.chatstore);
- }
- },
-
- // === //private// {{{AjaxIM.}}}**{{{_storeMessage(ab, chatbox, username, message, time)}}}** ===
- //
- // Taking the same arguments as {{{AjaxIM._addMessage}}}, {{{_storeMessage}}} pushes a message
- // into the storage hash, if storage is enabled.
- //
- // Messages are added to a message array, by username. The entire chat hash is stored as
- // a {{{'chats'}}} object in whatever storage engine is enabled.
- _storeMessage: function(ab, chatbox, username, message, time) {
- // If storage is on & ready, store the message
- if(this.settings.storageMethod) {
- if(!this.chatstore) this.chatstore = {};
-
- message = message.replace(//g, '>');
-
- 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();
- }
-
- // For some reason, if we don't encode and decode the message, it *will* break
- // (at least) the Flash storage engine's retrieval. Gah!
- this.chatstore[chatbox.data('username')].push(
- [ab, username, encodeURIComponent(message), time]);
-
- if(this.storageReady) $.jStore.store(this.storeKey + '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) {
- // add friend to buddylist, update their status, etc.
- var status_name = 'available';
- $.each(this.statuses,
- function(key, val) { if(status == val) { status_name = key; return false; } });
-
- if(this.chats[friend]) {
- var tab = this.chats[friend].data('tab');
- var tab_class = 'imjs-tab';
- if(tab.data('state') == 'active') tab_class += ' imjs-selected';
- tab_class += ' imjs-' + status_name;
-
- 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(AjaxIM.dateFormat(date_stamp_time.html()));
-
- var date_stamp_date = date_stamp.find('.imjs-date-date').html(
- AjaxIM.i18n[
- 'chat' + status_name[0].toUpperCase() + status_name.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]) {
- var friend_id = 'imjs-friend-' + $.md5(friend + this.friends[friend].group);
- $('#' + friend_id).attr('class', 'imjs-friend imjs-' + status_name);
-
- if(status == 0) {
- $('#' + 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);
- },
-
- // === {{{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(to, message) {
- if(!message) return;
-
- if(this.chats[to]) { // REMOVE ME?
- // possibly add a datestamp
- var time = self._addDateStamp(this.chats[to]);
- this._storeNonMessage('datestamp', to, null, time);
-
- time = this._addMessage('a', this.chats[to], this.username, message);
- this._storeMessage('a', this.chats[to], this.username, message, time);
- }
-
- $(self).trigger('sendingMessage', [to, message]);
-
- AjaxIM.request(
- this.actions.send,
- {'to': to, 'message': message},
- function(result) {
- switch(result.r) {
- case 'ok':
- $(self).trigger('sendMessageSuccessful', [to, message]);
- break;
-
- case 'offline':
- $(self).trigger('sendMessageFailed', ['offline', to, message]);
- break;
-
- case 'error':
- default:
- if(result.e == 'no session found') {
- self._notConnected();
- self._addError(self.chats[to], AjaxIM.i18n.notConnected);
- self._storeNonMessage('error', to,
- AjaxIM.i18n.notConnected, (new Date()).getTime());
- }
-
- $(self).trigger('sendMessageFailed', [result.e, to, message]);
- break;
- }
- },
- function(error) {
- self._notConnected();
- self._addError(self.chats[to], AjaxIM.i18n.notConnected);
- self._storeNonMessage('error', to,
- AjaxIM.i18n.notConnected, (new Date()).getTime());
-
- $(self).trigger('sendMessageFailed', ['not connected', to, message]);
- }
- );
- },
-
- // === {{{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(s, message) {
- // update status icon(s)
- if(!this.statuses[s])
- return;
-
- $('#imjs-friends').attr('class', 'imjs-' + s);
-
- $(self).trigger('changingStatus', [s, message]);
-
- AjaxIM.request(
- this.actions.status,
- {'status': this.statuses[s], 'message': message},
- function(result) {
- switch(result.r) {
- case 'ok':
- $(self).trigger('changeStatusSuccessful', [s, message]);
- break;
-
- case 'error':
- default:
- $(self).trigger('changeStatusFailed', [result.e, s, message]);
- break;
- }
- },
- function(error) {
- $(self).trigger('changeStatusFailed', ['not connected', s, message]);
- }
- );
- },
-
- // === {{{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: 0, available: 1, away: 2, invisible: 3},
-
- // == Footer bar ==
- //
- // The footer bar is the bar that sits at the bottom of the page, in a fixed
- // position. It contains a tab for the friends list, and tabs for any open
- // chat boxes. It is also possible to add custom tabs for other functionality.
- bar: {
- // === {{{AjaxIM.}}}**{{{bar.initialize()}}}** ===
- //
- // Setup the footer bar and enable tab actions. This function
- // uses {{{jQuery.live}}} to set hooks on any bar tabs created
- // in the future.
- initialize: function() {
- // Set up your standard tab actions
- $('.imjs-tab')
- .live('click', this.activateTab);
-
- $('.imjs-tab .imjs-close')
- .live('click', this.closeTab);
-
- // Set up the friends list actions
- var self = this;
- $(document).click(function(e) {
- if(e.target.id == 'imjs-friends' ||
- $(e.target).parents('#imjs-friends').length) {
- return;
- }
-
- if($('#imjs-friends').data('state') == 'active')
- self.activateTab.call($('#imjs-friends'));
- });
- $('#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(this);
- })
- .mouseenter(function() {
- if($(this).hasClass('imjs-not-connected')) {
- $('.imjs-tooltip').css('display', 'block');
- $('.imjs-tooltip p').html(AjaxIM.i18n.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')
- .data('tab', $('#imjs-friends'))
- .css('display', 'none');
- },
-
- // === {{{AjaxIM.}}}**{{{bar.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() {
- var chatbox = $(this).find('.imjs-chatbox') || false;
-
- if($(this).data('state') != 'active') {
- if($(this).attr('id') != 'imjs-friends') {
- $('#imjs-bar > li')
- .not($(this))
- .not('#imjs-friends')
- .removeClass('imjs-selected')
- .each(function() {
- if($(this).data('state') != 'closed') {
- $(this).data('state', 'minimized');
- var chatbox = $(this).find('.imjs-chatbox');
- if(chatbox.length)
- chatbox.css('display', 'none');
- }
- });
- }
-
- if(chatbox && chatbox.css('display') == 'none')
- chatbox.css('display', '');
-
- // set the tab to active...
- var tab = $(this).addClass('imjs-selected').data('state', 'active');
-
- // ...and hide and reset the notification icon
- tab.find('.imjs-notification').css('display', 'none')
- .data('count', 0);
-
- if(self.settings.storageMethod && self.storageReady &&
- chatbox && (username = chatbox.data('username'))) {
- $.jStore.store(self.storeKey + 'activeTab', [username]);
- }
-
- $(self).trigger('tabToggled', ['activated', tab]);
- } else {
- var tab = $(this).removeClass('imjs-selected').data('state', 'minimized');
-
- if(chatbox && chatbox.css('display') != 'none')
- chatbox.css('display', 'none');
-
- if(self.settings.storageMethod && self.storageReady) {
- $.jStore.store(self.storeKey + 'activeTab', ['*']);
- }
-
- $(self).trigger('tabToggled', ['minimized', tab]);
- }
-
- if(chatbox) {
- if(!(input = chatbox.find('.imjs-input')).data('height')) {
- // store the height for resizing later
- 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.}}}**{{{bar.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() {
- var tab = $(this).parents('.imjs-tab');
- tab.css('display', 'none').data('state', 'closed');
-
- if(self.settings.storageMethod && self.storageReady) {
- delete self.chatstore[tab.find('.imjs-chatbox').data('username')];
- if(self.storageReady) $.jStore.store(self.storeKey + 'chats', self.chatstore);
- }
-
- $(self).trigger('tabToggled', ['closed', tab]);
-
- self.bar._scrollers();
-
- return false;
- },
-
- // === {{{AjaxIM.}}}**{{{bar.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-right');
- tab.removeClass('imjs-default')
- .attr('id', 'imjs-tab-' + $.md5(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.data('chatbox', action);
- } else {
- tab.find('.imjs-chatbox').remove();
- tab.click(action);
- }
-
- return tab;
- },
-
- // === {{{AjaxIM.}}}**{{{bar.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.}}}**{{{bar._scrollers()}}}** ===
- //
- // Document me!
- _scrollers: function() {
- var needScrollers = false;
- $('.imjs-tab').filter(function() {
- return $(this).data('state') != 'closed'
- }).css('display', '');
-
- $.each(self.chats, function(username, chatbox) {
- var tab = chatbox.data('tab');
- if(tab.data('state') == 'closed') return true;
-
- if(tab.position().top > $('#imjs-bar').height()) {
- $('.imjs-scroll').css('display', '');
- tab.css('display', 'none');
- needScrollers = true;
- } else {
- tab.css('display', '');
- }
- });
-
- if(!needScrollers) {
- $('.imjs-scroll').css('display', 'none');
- }
-
- if($('#imjs-scroll-left').css('display') != 'none' &&
- $('#imjs-scroll-left').position().top > $('#imjs-bar').height()) {
- $('#imjs-bar li.imjs-tab:visible').slice(-1).css('display', 'none');
- }
-
- var hiddenLeft = $('#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 hiddenRight = $('#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);
- }
- },
-
- // == Comet ==
- //
- // Comet, or HTTP streaming, holds open a connection between the client and
- // the server indefinitely. As the server receives new messages or events,
- // they are passed down to the client in a {{{<script>}}}
- // tag which calls the {{{AjaxIM.incoming()}}} function. The connection is
- // opened using either an {{{iframe}}} (in Opera or Internet Explorer) or
- // an {{{XMLHTTPRequest}}} object. Due to the extra flexibility necessary
- // with the {{{XMLHTTPRequest}}} object, jQuery's {{{$.ajax}}} is not used.
- comet: {
- type: '',
- obj: null,
-
- onbeforeunload: null,
- onreadystatechange: null,
-
- iframe: function() {
- if(/loaded|complete/i.test(this.obj.readyState))
- throw new Error("IM server not available!");
- },
-
- // === {{{AjaxIM.}}}**{{{comet.connect()}}}** ===
- //
- // Creates and initializes the object and connection between the server
- // and the client. For Internet Explorer and Opera, we use an
- // {{{<iframe>}}} element; for all other browsers, we create an
- // {{{XMLHTTPRequest}}} object. The server connects to the URI defined
- // as the "poll" action. This function is called automatically, when
- // the IM engine is initialized and the {{{AjaxIM.poll()}}} function
- // is called.
- connect: function() {
- var self = _instance;
-
- if($.browser.opera || $.browser.msie) {
- var iframe = $('');
- with(iframe) {
- css({
- position: 'absolute',
- visibility: 'visible',
- display: 'block',
- left: '-10000px',
- top: '-10000px',
- width: '1px',
- height: '1px'
- });
-
- attr('src', self.actions.poll[1]);
- appendTo('body');
-
- bind('readystatechange',
- self.comet.onreadystatechange = function() { self.comet.iframe() });
- bind('beforeunload',
- self.comet.onbeforeunload = function() { self.comet.disconnect() });
- }
-
- self.comet.type = 'iframe';
- self.comet.obj = iframe;
- } else {
- var xhr = new XMLHttpRequest;
- var length = 1029;
- var code = /^\s*
-
-
-
-
-