Home » Demos » TCPSocket Demo (IRC)

TCPSocket Demo (IRC)

IRC @ freenode
 
0 users
jchavarria_work


About this demo


This demo is an exemple of the implementation of the TCPSockets in APE. Here, APE is used as a middleware, making it the gateway to an IP:Port : IRC.

Which features this demo is using?

  • proxy.js
  • ServerSide JavaScript



A simple chat demo, multi-user and private messages.

APE real time chat

A simple chat demo, multi-user and private messages.

Demo of multi-user moving and chating in real time.

Move demo

Demo of multi-user moving and chating in real time.

Live tweets from Twitter trends, in real time!

Live tweets

Live tweets from Twitter trends, in real time!


Gateway to IRC using the TCPSocket feature. Come chat with us ;)

TCPSocket Demo (IRC)

Gateway to IRC using the TCPSocket feature. Come chat with us ;)

A demo of a small massive multiplayer role playing game!

MMORPG

A demo of a small massive multiplayer role playing game!

Draw with everyone in real time!

Pixelbox

Draw with everyone in real time!


Homepage demo

Homepage demo

This is the demo from the home page of APE, but you've probably already seen it ;-)


Study the source code


Check out the Client JavaScript, HTML and ServerSide JavaScript source code of this demo by reading the following :


  1. <link rel="stylesheet" type="text/css" href="/demos/IRC/irc.css" />
  2. <script type="text/javaScript" src="/demos/IRC/irc.js"></script>
  3. <script type="text/javaScript" src="/demos/IRC/apeirc.js"></script>
  4. <script type="text/javaScript" src="/demos/IRC/channel.js"></script>
  5.  
  6. <script type="text/javascript">
  7.  
  8.     function rand_chars(){
  9.         var keylist="abcdefghijklmnopqrstuvwxyz"
  10.                 var temp=''
  11.                 var plength=8;
  12.         for (i=0;i<plength;i++){
  13.                 temp+=keylist.charAt(Math.floor(Math.random()*keylist.length))
  14.         }
  15.         return temp;
  16.    }
  17.    function setNick(){
  18.        var nick = $('nick_input').value.replace(' ', '');
  19.        if(nick.length == 0){
  20.            alert('Veuillez entrer un login');
  21.        }else{
  22.            if( irc_client.setNick(nick.substr(0, 15)) ){
  23.                 var login = $('login');
  24.                 login.set('tween', {duration: 'short'});
  25.                 login.tween('opacity', 0);
  26.                                 setNick = function(){};
  27.                         }
  28.        }
  29.    }
  30.  
  31.    var irc_client = new APE.IrcClient();
  32.    window.addEvent('domready', function(){
  33.        $('nick_input').focus();
  34.        irc_client.load({
  35.            'domain': APE.Config.domain,
  36.            'server': APE.Config.server,
  37.            'identifier':'ircdemo',
  38.            'scripts': APE.Config.scripts,
  39.            'complete': function(ape){
  40.                irc_client.complete();
  41.            }
  42.        });
  43.    });
  44. </script>
  45. <div id="ircchat">
  46.     <div id="login">
  47.         <div class="popup">
  48.             <p>Choose a nickname to sign in to IRC on freenode</p>
  49.             Your nickname : <input maxlength="15" id="nick_input" onkeypress="if (event.keyCode == 13) setNick();" <?php if(isset($_GET['login'])){ echo 'value="'.htmlentities($_GET['login']).'"'; }?> type="text" /> <button onclick="setNick();">Ok</button>
  50.         </div>
  51.     </div>
  52.     <div class="header">
  53.         <div class="server">IRC @ freenode</div>
  54.         <div id="tabs">
  55.         </div>
  56.     </div>
  57.     <div class="chat">
  58.         <div class="texte-zone">
  59.             <div id="line">&nbsp;</div>
  60.             <div class="texte" id="history"></div>
  61.         </div>
  62.         <div class="list">
  63.             <div class="header"><span id="usr_cnt">0</span> users</span></div>
  64.             <div class="user-list">
  65.                 <div id="user-list">
  66.                 </div>
  67.             </div>
  68.         </div>
  69.         <div class="input-zone">
  70.             <button id="chatButton" onclick="irc_client.sendClick()">Ok</button>
  71.             <input type="text" id="chat-input" />
  72.             <div class="show-nick">jchavarria_work</div>
  73.         </div>
  74.     </div>
  75. </div>
  76.  
  1. /**
  2.  * This demo uses APE to create a Vitual TCPSocket and
  3.  * connect to an irc server (Its transparent, but it's the ape server which
  4.  * connects to the irc server).
  5.  *
  6.  *
  7.  */
  8.  
  9. var TCPSocket;
  10. APE.IrcClient = new Class({
  11.         Extends: APE.Client,
  12.         Implements: Options,
  13.         irc: null,
  14.         connected: false,
  15.         joined: false,
  16.         currentChannel: null,
  17.         version: '0.1',
  18.         options: {
  19.                 container: document.body,
  20.            /*
  21.            irc_server: '10.1.0.30',
  22.            irc_port: 6667,
  23.            */
  24.            
  25.            irc_server: 'irc.freenode.net',
  26.            irc_port: 6667,
  27.            /*
  28.  
  29.                 irc_server: 'Vancouver.BC.CA.Undernet.org',
  30.                 irc_port: 6667,
  31.            */
  32.                
  33.                 original_channels:['#ape-test','#ape-project'],
  34.                 systemChannel: '@freenode',
  35.                 systemUser: '*System*',
  36.                 helpUser: '*HELP*'
  37.         },
  38.         els: {},
  39.         channels: new Hash(),
  40.         initialize: function(options){
  41.                 this.currentChannel = this.options.systemChannel;
  42.  
  43.                 this.setOptions(options);
  44.                 this.joinVirtualChannel(this.options.systemChannel);
  45.  
  46.                
  47.         },
  48.         joinVirtualChannel: function(chan, irc, buildTabs, doNotClose){
  49.                 this.setChannel(chan, new APE.IrcClient.Channel(chan, irc, doNotClose));
  50.                 if(buildTabs){
  51.                         this.buildTabs();
  52.                 }
  53.         },
  54.         joinUserChannel: function(user){
  55.                 if(!this.hasChannel(user)){
  56.                         this.joinVirtualChannel(user, this.irc, true, true);
  57.                 }
  58.         },
  59.         complete: function(){
  60.                
  61.                 this.core.start({name:rand_chars()});
  62.                 this.onRaw('login', this.initPlayground);
  63.         },
  64.         initPlayground: function(){
  65.  
  66.                
  67.                
  68.                 this.els.chatZone = $('history');
  69.                 this.els.tabs = $('tabs');
  70.                 this.els.input = $('chat-input');
  71.                 this.els.userList = $('user-list');
  72.                 this.els.userCnt = $('usr_cnt');
  73.  
  74.                 //Adding special events
  75.                 this.els.input.addEvent('keydown', this.sendKey.bindWithEvent(this));
  76.  
  77.  
  78.                 //TCPSocket implementation
  79.                 TCPSocket = this.core.TCPSocket;
  80.  
  81.                 //IRC events
  82.                 this.irc = new IRCClient();
  83.                 this.irc.onopen  = this.onopen.bind(this);
  84.                 this.irc.onclose = this.onclose.bind(this);
  85.                 this.irc.onTOPIC = this.onTOPIC.bind(this);
  86.                 this.irc.onNICK = this.onNICK.bind(this);
  87.                 this.irc.onJOIN = this.onJOIN.bind(this);
  88.                 this.irc.onQUIT = this.onQUIT.bind(this);
  89.                 this.irc.onPART = this.onPART.bind(this);
  90.                 this.irc.onACTION = this.onACTION.bind(this);
  91.                 this.irc.onCTCP  = this.onCTCP.bind(this);
  92.                 this.irc.onNOTICE  = this.onNOTICE.bind(this);
  93.                 this.irc.onPRIVMSG = this.onPRIVMSG.bind(this);
  94.                 this.irc.onMODE = this.onMODE.bind(this);
  95.                 this.irc.onERROR = this.onerror.bind(this);
  96.                 this.irc.onerror = this.onerror.bind(this);
  97.                 this.irc.onresponse = this.onresponse.bind(this);
  98.  
  99.  
  100.         },
  101.         changeMyNick: function(nick){
  102.                 this.nickname = nick;
  103.                
  104.                 $$('.show-nick').set('html', nick);
  105.         },
  106.         setNick: function(nickname){
  107.                 if(!this.irc) return false;
  108.                 nickname = this.cleanNick(nickname);
  109.                 this.irc.connect(this.options.irc_server, this.options.irc_port);
  110.                 this.changeMyNick(nickname);
  111.                 return true;
  112.         },
  113.         /** CHANNELS ACCESSORS */
  114.         getChannel: function(key){
  115.                 return this.channels.get(this.toChan(key));
  116.         },
  117.         hasChannel: function(key){
  118.                 return this.channels.has(this.toChan(key));
  119.         },
  120.         setChannel: function(key, chan){
  121.                 this.channels.set(this.toChan(key), chan);
  122.         },
  123.         eraseChannel: function(key){
  124.                 this.channels.erase(this.toChan(key));
  125.         },
  126.         compareChannels: function(a, b){
  127.                 return this.toChan(a) == this.toChan(b);
  128.         },
  129.         /**
  130.          * connect() is called two times, but only does something when irc is connected
  131.          * AND nickname is set
  132.          */
  133.         connect: function(){
  134.                 if(this.nickname && this.connected){
  135.                         this.irc.ident(this.nickname, '8 *', this.nickname);
  136.                         this.irc.nick(this.nickname);
  137.                 }
  138.         },
  139.         onopen: function(){
  140.                
  141.                 this.connected = true;
  142.                 this.connect();
  143.                 /*
  144.                 window.addEvent('unload', function(){
  145.                         this.irc.reset();
  146.                 }.bind(this));
  147.                 */
  148.         },
  149.         onclose: function(){
  150.                
  151.         },
  152.         onerror: function(cmd){
  153.                 if(cmd.args[0] == "Closing Link: 127.0.0.1 (Connection Timed Out)"){
  154.                         this.showInfo('Error occured, reconnecting...', 'error');
  155.                         this.irc.connect(this.options.irc_server, this.options.irc_port);
  156.                         return;
  157.                 }
  158.                 var responseCode = parseInt(cmd.type);
  159.                 if (responseCode == 431 || responseCode == 432 || responseCode == 433) {
  160.                 // 431   ERR_NONICKNAMEGIVEN
  161.                 // 432   ERR_ERRONEUSNICKNAME
  162.                 // 433   ERR_NICKNAMEINUSE
  163.                         var nick = cmd.args[1];
  164.  
  165.                         if(responseCode == 432){
  166.                                 nick = this.cleanNick(nick);
  167.                                 this.changeMyNick(nick);
  168.                         }else
  169.                                 if(nick.length == 15){
  170.  
  171.                                 }
  172.  
  173.                                 nick = (nick.length>= 15?nick.substr(1, 14):nick) + '_';
  174.                                 this.changeMyNick(nick);
  175.                                 this.irc.nick(nick);
  176.                                 //this.irc.ident(this.nickname, '8 *', this.nickname);
  177.                 }else if(responseCode == 451){
  178.                         this.irc.ident(this.nickname, '8 *', this.nickname);
  179.                 }else{
  180.                         this.showInfo(this.sanitize(cmd.args.pop()), 'error');
  181.                 }
  182.         },
  183.         onresponse: function(cmd){
  184.                 var responseCode = parseInt(cmd.type);
  185.                 if(responseCode==372){
  186.                         this.addMessage(this.options.systemChannel, this.options.systemUser, cmd.args[1].substr(2), 'info');
  187.                        
  188.                         if(!this.joined){
  189.                                 var cnt = this.options.original_channels.length;
  190.  
  191.                                 for(var i = 0; i < cnt; i ++) {
  192.                                         this.joinChannel(this.options.original_channels[i], i==0);
  193.                                 }
  194.                                
  195.                         }
  196.                 }else if(responseCode==366){
  197.                         this.addMessage(this.options.systemChannel, this.options.systemUser, 'Joined '+cmd.args[1]+' channel.', 'info user-join');
  198.                 }
  199.                 else if(responseCode==353){
  200.                         var channel = cmd.args[2];
  201.                         var userList = cmd.args[3].split(/\s+/);
  202.                        
  203.                         userList.each(function(user){
  204.                                 if(user != '')
  205.                                         this.addUser(channel, user, false, false);
  206.                         }.bind(this));
  207.                         if(this.compareChannels(channel,this.currentChannel)){
  208.                                 var chan = this.getCurrentChannel();
  209.                                 chan.sortUsers();
  210.                                 this.buildUsers();
  211.                         }
  212.  
  213.                 }
  214.                 else if(responseCode==332){
  215.                         this.addMessage(cmd.args[1], this.options.systemUser, ['Topic for '+cmd.args[1]+' is: ',new Element('i',{'text':this.sanitize(cmd.args[2])})],'info topic');
  216.                 }
  217.                 else if(responseCode==333){
  218.                         var d = new Date();
  219.                         var time = parseInt(cmd.args[3], 10);
  220.                         d.setTime(time*1000);
  221.                         var when = d.toString();
  222.                         this.addMessage(cmd.args[1], this.options.systemUser, ['Topic for '+cmd.args[1]+' was set by ',this.userLink(this.parseName(cmd.args[2])),' on '+when], 'info topic');
  223.                 }
  224.         },
  225.         /**
  226.          * NAMES
  227.          * CTCP VERSION
  228.          */
  229.         onJOIN: function(cmd){
  230.                 var chan = cmd.args[0];
  231.                 var user = this.parseName(cmd.prefix);
  232.                 this.addUser(chan, user);
  233.         },
  234.         onMODE: function(cmd){
  235.                
  236.                 var chan = this.getChannel(cmd.args[0]);
  237.                 if(chan){
  238.                         var from = this.parseName(cmd.prefix);
  239.                         var user = cmd.args[2];
  240.                         var op = cmd.args[1];
  241.                         if(op.substr(0,1)=='+'){
  242.                                 var build = false;
  243.                                 if(op.contains('o')){
  244.                                         this.addMessage(chan, this.options.systemUser,
  245.                                         [this.userLink(from),' gives channel operator status to ',this.userLink(user)], 'info status');
  246.                                         chan.renameUser(user, '@'+user);
  247.                                         build = true;
  248.                                 }else if(op.contains('v')){
  249.                                         this.addMessage(chan, this.options.systemUser,
  250.                                         [this.userLink(from),' gives channel operator status to ',this.userLink(user)], 'info status');
  251.                                         chan.renameUser(user, '+'+user);
  252.                                         build = true;
  253.                                 }
  254.                                 if(build){
  255.                                         this.buildUsers();
  256.                                 }
  257.                         }
  258.                 }
  259.         },
  260.         onPART: function(cmd){
  261.                 var chan = this.getChannel(cmd.args[0]);
  262.                 if(chan){
  263.                         var user = this.parseName(cmd.prefix);
  264.                         var msg = String(cmd.args[1]);
  265.  
  266.                         chan.remUser(user);
  267.                         this.addMessage(chan.name, this.options.systemUser, user+' has left '+chan.name+(msg.length>0?' ('+msg+')':''), 'user-left');
  268.                         if(chan.name==this.currentChannel){
  269.                                 this.remUser(user);
  270.                         }
  271.                 }
  272.         },
  273.         onQUIT: function(cmd){
  274.                 var user = this.parseName(cmd.prefix);
  275.                 this.channels.each(function(chan){
  276.                         if(chan.remUser(user)){
  277.                                 this.addMessage(chan.name, this.options.systemUser, [this.userLink(user),' has quit ('+this.sanitize(cmd.args[0])+')'], 'info user-left');
  278.                                 if(chan.name==this.currentChannel){
  279.                                         this.remUser(user);
  280.                                 }
  281.                         }
  282.                 }.bind(this));
  283.         },
  284.         onNICK: function(cmd){
  285.                 this.changeNick(this.parseName(cmd.prefix), cmd.args[0]);
  286.         },
  287.         onTOPIC: function(cmd){
  288.                 var user = this.parseName(cmd.prefix);
  289.                 this.addMessage(cmd.args[0], this.options.systemUser, user+' has changed the topic to: '+cmd.args[1], 'info topic');
  290.         },
  291.         onCTCP: function(cmd){
  292.                 if(cmd.args[1]=='VERSION'){
  293.                         this.irc.ctcp(this.parseName(cmd.prefix), 'VERSION APE TCPSocket Demo (IRC) 2 - http://www.ape-project.com v'+this.version+' On a Web Browser (' + navigator.appCodeName + ')', true);
  294.                 }else{
  295.                        
  296.                 }
  297.         },
  298.         onACTION: function(cmd){
  299.                 var user = this.parseName(cmd.prefix);
  300.                 var args = cmd.args;
  301.                 this.addMessage(args.shift(), this.options.systemUser, [this.userLink(user),' '+this.sanitize(args.join(' '))], 'info action');
  302.         },
  303.         onNOTICE: function(cmd){
  304.                 var msg = cmd.args[1];
  305.                 if(msg.charCodeAt(0)==1 && msg.charCodeAt(msg.length - 1) == 1){
  306.                         // CTCP REPLY
  307.                         msg = msg.substr(1,msg.length-2).split(' ');
  308.                         this.addCTCP(cmd.args[0], msg.shift(), msg.join(' '));
  309.                 }else{
  310.                         if(cmd.prefix && cmd.prefix.substr(0,3)=='irc')
  311.                                 this.addMessage(this.options.systemChannel, this.options.systemUser, this.sanitize(cmd.args[1]), 'info notice');
  312.                         else
  313.                                 this.showInfo(this.sanitize(cmd.args[1]), 'notice');
  314.                 }
  315.         },
  316.         onPRIVMSG: function(cmd){
  317.                 var from = this.parseName(cmd.prefix);
  318.                 var chan = cmd.args[0];
  319.                 var msg = cmd.args[1];
  320.  
  321.                 if(this.compareChannels(chan, this.nickname)){
  322.                         this.joinUserChannel(from);
  323.                         this.addUser(from, from);
  324.                         chan = from;
  325.                 }
  326.                 this.addMessage(chan, from, this.sanitize(msg));
  327.         },
  328.         addCTCP: function(who, what, txt){
  329.                 if(txt != undefined){
  330.                         this.addMessage(this.currentChannel, '-'+who+'-', 'CTCP '+what+' REPLY: '+txt, 'ctcp');
  331.                 }else{
  332.                         this.addMessage(this.currentChannel, '>'+who+'<', 'CTCP '+what, 'ctcp')
  333.                 }
  334.         },
  335.         getCurrentChannel: function(){
  336.                 return this.getChannel(this.currentChannel);
  337.         },
  338.         joinChannel: function(channel, switchTo){
  339.                
  340.                 if(!this.hasChannel(channel)){
  341.                         var chan = new APE.IrcClient.Channel(channel, this.irc);
  342.                         this.setChannel(channel, chan);
  343.                         chan.join();
  344.                         if(switchTo!==false) this.switchTo(channel);
  345.                         else this.buildTabs();
  346.                 }else if(switchTo!==false){
  347.                         this.switchTo(channel);
  348.                 }
  349.                 this.joined = true;
  350.         },
  351.         switchTo: function(chan){
  352.  
  353.                 if(!this.hasChannel(chan)){
  354.                         return;
  355.                 }
  356.                 var channel = this.getChannel(chan);
  357.  
  358.                 /** For history */
  359.                 channel.Hcnt = 0;
  360.  
  361.                 this.currentChannel = chan;
  362.  
  363.                 /* Updating messages and users*/
  364.            this.els.chatZone.getElements('div.msg_line').dispose();
  365.  
  366.  
  367.                 this.buildTabs();
  368.                
  369.                 this.buildUsers();
  370.  
  371.                 this.checkLine();
  372.                
  373.                 this.buildMsg();
  374.         },
  375.         cleanNick: function(nick){
  376.                 var ret = String(nick).replace(/[^a-zA-Z0-9\-_~]/g, '');
  377.                 ret = ret.replace(/^[0-9]+/, '');
  378.                 if(ret=='') return 'Guest'+Math.round(Math.random()*100);
  379.                
  380.                 if(nick != ret) this.showError('Invalid nick "'+this.sanitize(nick)+'" changed to "'+ret+'".');
  381.                 return ret;
  382.         },
  383.         changeNick: function(from, to){
  384.                 this.channels.each(function(chan){
  385.                         if(chan.renameUser(from, to)){
  386.                                 this.addMessage(chan.name, this.options.systemUser, [this.userLink(from)," is now known as ", this.userLink(to)], 'info');
  387.                         }
  388.                         if(this.compareChannels(chan.name, from)){
  389.                                 this.changeChannelName(from, to);
  390.                         }
  391.                 }.bind(this));
  392.                 if(this.compareChannels(from, this.nickname))
  393.                         this.changeMyNick(to);
  394.                 this.buildUsers();
  395.         },
  396.         changeChannelName: function(from, to){
  397.  
  398.                 var chan = this.getChannel(from);
  399.                 this.eraseChannel(from);
  400.                 chan.name = to;
  401.                 this.setChannel(to, chan);
  402.                 if(this.compareChannels(from, this.currentChannel)){
  403.                         this.currentChannel = from;
  404.                 }
  405.                 this.buildTabs();
  406.         },
  407.         parseName: function(identity){
  408.                 return identity.split("!", 1)[0];
  409.         },
  410.         remUser: function(user){
  411.                 var go = new Fx.Morph($('user_line_'+user));
  412.                 go.start({
  413.                         'height': 0
  414.                 });
  415.                 go.addEvent('complete', function(el){
  416.                         el.destroy();
  417.                 })
  418.  
  419.                 this.userCntAdd(-1);
  420.         },
  421.         addUser: function(chan, user, showMessage, addNow){
  422.  
  423.                 var channel =  this.getChannel(chan);
  424.                 if(!channel){
  425.                         return;
  426.                 }
  427.                 var add;
  428.                 if(add = channel.addUser(user, addNow)){
  429.                         if(this.compareChannels(chan, this.currentChannel) && addNow !== false){
  430.  
  431.                                 if(channel.getLastUser().type != 'zzz' || add == 'rename'){
  432.                                         this.buildUsers();
  433.                                 }else{
  434.                                         this.writeUser(channel.buildLastUser( this.userClick.bindWithEvent(this) ));
  435.                                         this.userCntAdd(1);
  436.                                 }
  437.                         }
  438.                         if(showMessage !== false)
  439.                                 this.addMessage(chan, this.options.systemUser, [this.userLink(user),' has joined'+(chan==user?'':' '+chan)], 'info user-join');
  440.                 }
  441.         },
  442.         writeUser: function(el_user){
  443.                 this.els.userList.adopt(el_user);
  444.         },
  445.         addMessage: function(chan, user, txt, special){
  446.                 var channel = this.getChannel(chan);
  447.                 if(channel){
  448.                         if(user != this.options.systemUser) user = this.userLink(user);
  449.                         channel.addMessage(user, txt, special, this.nickname);
  450.                         if(this.compareChannels(chan, this.currentChannel)){
  451.                                 this.writeMsg(channel.buildLastMessage());
  452.                         }
  453.                 }
  454.         },
  455.         writeMsg: function(el_line){
  456.                 this.els.chatZone.adopt(el_line);
  457.                 this.scrollBottom();
  458.                 this.checkLine();
  459.         },
  460.         checkLine: function(){
  461.                 var chan = this.getCurrentChannel();
  462.                 var lineW = chan.getLineWidth();
  463.                 var textW = 590 - lineW;
  464.  
  465.                 var line_size = Math.round((textW - 10) / 6);
  466.                 chan.line_size = line_size;
  467.  
  468.                 // TODO Resize long lines
  469.  
  470.                 $$('.msg_user').tween('width', lineW);
  471.                 $('line').tween('left', lineW);
  472.                 $$('.msg_text').tween('width', textW);
  473.         },
  474.         scrollBottom: function(){
  475.                 var scrollSize = this.els.chatZone.getScrollSize();
  476.  
  477.                 this.els.chatZone.scrollTo(0,scrollSize.y);
  478.         },
  479.         sendMsg: function(msg, checkCmd){
  480.  
  481.                 var channel = this.getCurrentChannel();
  482.  
  483.                 channel.history.unshift(msg);
  484.                 channel.Hcnt = 0;
  485.  
  486.                 //This is a command
  487.                 if(checkCmd !== false && msg.substr(0,1)=='/')
  488.                         this.sendCmd(msg.substr(1).split(' '));
  489.                 else{
  490.                         channel.send(msg);
  491.                         // the IRC server will not echo our message back, so simulate a send.
  492.                         this.onPRIVMSG({prefix:this.nickname,type:'PRIVMSG',args:[this.currentChannel, msg]});
  493.                 }
  494.         },
  495.         sendCmd: function(args){
  496.                 switch(String(args[0]).toLowerCase()){
  497.                         case 'j':
  498.                         case 'join':
  499.                                 if(!args[1] || !args[1].match(/^(#|&)[^ ,]+$/))
  500.                                         this.showError('Invalid chan name "'+this.sanitize(args[1])+'" chan name must begin with # or & and must not contains any of \' \' (space) or \',\' (comma) and must contains at least 2 chars.')
  501.                                 else
  502.                                         this.joinChannel(args[1])
  503.                                 break;
  504.                         case 'clear':
  505.                                 if(!args[1] || args[1]!='ALL'){
  506.                                         this.getCurrentChannel().clear();
  507.                                 }else{
  508.                                    this.channels.each(function(chan){
  509.                                            chan.clear();
  510.                                    });
  511.                                 }
  512.                                 this.els.chatZone.empty();
  513.                                 break;
  514.                         case 'quit':
  515.                                 args.shift();
  516.                                 window.location.reload();
  517.                                 this.irc.quit(args.join(' '));
  518.                                 break;
  519.                         case 'ctcp':
  520.                                 args.shift();
  521.                                 var user = args.shift();
  522.  
  523.                                 this.irc.ctcp(user, args.join(' '));
  524.                                 this.addCTCP(user, args[0]);
  525.                                 break;
  526.                         case 'me':
  527.                         case 'action':
  528.                                 if(!args[1]){
  529.                                         this.showError('Invalid arguments, /help for more informations');
  530.                                 }
  531.                                 args.shift();
  532.                                 this.irc.action(this.currentChannel, args.join(' '));
  533.                                 this.onACTION({prefix:this.nickname,args:[this.currentChannel, args.join(' ')]});
  534.                                 break;
  535.                         case 'msg':
  536.                         case 'privmsg':
  537.                                 if(args[1] == undefined || args[2] == undefined ){
  538.                                         this.showError('Invalid arguments, /help for more informations');
  539.                                         break;
  540.                                 }
  541.                                 args.shift();
  542.  
  543.                                 var to = String(args.shift());
  544.                                 if(to.charAt(0)=='@') to = to.substr(1);
  545.                                
  546.                                 var msg = args.join(' ');
  547.  
  548.                                 this.joinUserChannel(to);
  549.                                
  550.                                 this.switchTo(to);
  551.                                 this.sendMsg(msg, false);
  552.                                
  553.                                 break;
  554.                         case 'nick':
  555.                                 this.irc.nick(args[1]);
  556.                                 //this.changeMyNick(args[1]);
  557.                                 break;
  558.                         case 'h':
  559.                         case 'help':
  560.                                 this.addMessage(this.currentChannel, this.options.helpUser,'\
  561. List of available commands :\n\
  562. \n\
  563.         /HELP , show this help.\n\
  564.         /H , alias for /HELP.\n\
  565.         /JOIN <channel>, joins the channel.\n\
  566.         /CLEAR [ALL], clear current channels, clears messages and input history.\n\
  567.         /QUIT [<reason>], disconnects from the server.\n\
  568.         /CTCP <nick> <message>, send a CTCP message to nick, VERSION and USERINFO are commonly used.\n\
  569.         /ACTION <action>, send a CTCP ACTION message, describing what you are doing.\n\
  570.         /PRIVMSG [@]<nick> <message>, sends a private message.\n\
  571.         /MSG [@]<nick> <message>, alias for /PRIVMSG.\n\
  572.         /NICK <nick>, sets you nickname.\n\
  573. \n\
  574. Commands are case insensitive, for example you can user /join or /JOIN.','help')
  575.                                 break;
  576.                         default:
  577.                                 this.showError('Unknow or unsuported command "'+this.sanitize(args[0])+'".');
  578.                 }
  579.         },
  580.         showInfo: function(msg, type){
  581.                 this.addMessage(this.currentChannel, this.options.systemUser, msg, type);
  582.         },
  583.         showError: function(msg){
  584.                 this.showInfo(msg, 'error')
  585.         },
  586.         sendKey: function(ev){
  587.                 if(ev.key == 'enter') this.sendClick();
  588.                 else if(ev.key == 'up'){
  589.                         var chan = this.getCurrentChannel();
  590.                         if(chan.Hcnt == 0){
  591.                                 chan.currMsg = this.els.input.value;
  592.                         }
  593.                         if(chan.history.length > chan.Hcnt){
  594.                                 var i = chan.Hcnt++;
  595.                                 this.els.input.value = chan.history[i];
  596.                         }
  597.                 }else if(ev.key == 'down'){
  598.                         var chan = this.getCurrentChannel();
  599.                         if(chan.Hcnt > 0){
  600.                                 var i = --chan.Hcnt;
  601.  
  602.                                 this.els.input.value = i==0?chan.currMsg:chan.history[i-1];
  603.                         }
  604.                 }else if(ev.key=='tab'){
  605.                         ev.stop();
  606.                         var val = this.els.input.value;
  607.                         if(val.charAt(0)=='/' && !val.contains(' ')){
  608.                                 val = val.substr(1);
  609.                                 var cmd_list = new Array(
  610.                                         'help',
  611.                                         'join',
  612.                                         'clear',
  613.                                         'quit',
  614.                                         'ctcp',
  615.                                         'action',
  616.                                         'privmsg',
  617.                                         'nick',
  618.                                         'msg'
  619.                                 );
  620.                                 var check = function(cmd, index,array){
  621.                                         if(cmd.substr(0, val.length)==val){
  622.                                                 return true;
  623.                                         }
  624.                                         return false;
  625.                                 }
  626.                                 var ok = cmd_list.filter(check);
  627.                                 if(ok.length > 0){
  628.                                         this.els.input.value = '/'+ok[0].toUpperCase()+' ';
  629.                                 }
  630.                         }else{
  631.                         val = val.split(' ');
  632.                         var mot = val.pop();
  633.                         if(mot.charAt(0)=='@') mot = mot.substr(1);
  634.                                 if(mot.length > 0){
  635.                                         var chan = this.getCurrentChannel();
  636.                                         var usr = chan.search(mot);
  637.                                         if(usr){
  638.                                                 this.els.input.value = val.join(' ')+(val.length > 0?' ':'')+'@'+usr+' ';
  639.                                         }
  640.                                 }
  641.                         }
  642.                 }
  643.         },
  644.         sendClick: function(){
  645.  
  646.                 var value = this.els.input.value;
  647.                 if(value.length > 0){
  648.                         if(value.substr(0, 1)=='/' || this.currentChannel != this.options.systemChannel){
  649.                                 this.sendMsg(this.els.input.value);
  650.                         }else{
  651.                                 this.showError('No channel joined. Try /join #<channel>');
  652.                         }
  653.                 }
  654.                 this.els.input.value = '';
  655.         },
  656.         tabClick: function(event, chan){
  657.                 this.switchTo(chan);
  658.         },
  659.         tabCloseClick: function(event, chan){
  660.                 this.closeTab(chan);
  661.                 event.stop();
  662.         },
  663.         buildTabs: function(){
  664.                 /*
  665.                 <div class="tab">
  666.                         <sa class="close"></a>
  667.                         <span class="link">
  668.                                 #ape-project
  669.                         </span>
  670.                 </div>
  671.                  */
  672.                 var current = this.currentChannel;
  673.                 this.els.tabs.empty();
  674.                 var tabs = new Array();
  675.                 this.channels.each(function(chan, key){
  676.                         var el_tab = new Element('div', {
  677.                                 'class': 'tab'+(this.compareChannels(chan.name, current)?' current':'')+(this.compareChannels(key, this.options.systemChannel)?' sys':''),
  678.                                 'id': 'tab_'+key
  679.                         });
  680.                         var el_link = new Element('span', {
  681.                                 'text': chan.name,
  682.                                 'class': 'link'
  683.                         });
  684.                         var el_close = new Element('a', {
  685.                                 'href': '#'
  686.                         });
  687.                         el_tab.grab(el_close);
  688.                         el_tab.grab(el_link);
  689.  
  690.                         if(!this.compareChannels(chan.name, current))
  691.                                 el_link.addEvent('click', this.tabClick.bindWithEvent(this, key));
  692.                        
  693.                         if(!this.compareChannels(chan.name, this.options.sysChannel))
  694.                                 el_close.addEvent('click', this.tabCloseClick.bindWithEvent(this, key));
  695.  
  696.                         tabs.push(el_tab);
  697.                 }.bind(this));
  698.                 this.els.tabs.adopt(tabs);
  699.                 this.els.input.focus();
  700.         },
  701.         closeTab: function(tab){
  702.                
  703.                 if(this.compareChannels(tab,this.currentChannel)){
  704.                         var keys = this.channels.getKeys();
  705.                         var i = keys.indexOf(tab);
  706.                         var k = 0;
  707.                         if(i==keys.length-1){
  708.                                 k = i - 1;
  709.                         }else{
  710.                                 k = i+1;
  711.                         }
  712.                         this.switchTo(keys[k]);
  713.                 }
  714.                 var chan = this.getChannel(tab);
  715.                 chan.close('');
  716.                 this.eraseChannel(tab);
  717.                 $('tab_'+tab).destroy();
  718.         },
  719.         buildUsers: function(){
  720.                 this.els.userList.empty();
  721.  
  722.                 var users = this.getCurrentChannel().buildUsers(this.userClick.bindWithEvent(this));
  723.                 this.writeUser(users);
  724.                 this.userCntSet(users.length);
  725.         },
  726.         userCntSet: function(cnt){
  727.                 this.els.userCnt.set('text', cnt);
  728.                 this.els.userCnt.store('cnt', cnt);
  729.         },
  730.         userCntAdd: function(num){
  731.                 var cnt = this.els.userCnt.retrieve('cnt', 0);
  732.                 this.userCntSet(cnt+num);
  733.         },
  734.         userClick: function(ev, e2){
  735.                 ev.stop();
  736.                 var to = String(e2).replace(/^(@|\+)/g, '');
  737.                 if(!this.hasChannel(to)){
  738.                         this.joinVirtualChannel(to, this.irc, true, true);
  739.                 }
  740.                 this.switchTo(to);
  741.         },
  742.         buildMsg: function(){
  743.                 this.writeMsg(this.getCurrentChannel().buildMessages());
  744.         },
  745.         toChan: function(str){
  746.                 return String(str).toLowerCase();
  747.         },
  748.         userLink: function(user){
  749.                 var link = new Element('a', {
  750.                         'text': user,
  751.                         'class': 'user',
  752.                         'href': '#'
  753.                 });
  754.                 link.addEvent('click', this.userClick.bindWithEvent(this, user));
  755.                 return link;
  756.         },
  757.         sanitize: function(str){
  758.                 //all text elements are grabed via appendText, so there is no need to escape
  759.                 return str;
  760.         }
  761.         /*
  762.         sanitize: (function(str) {
  763.           // See http://bigdingus.com/2007/12/29/html-escaping-in-javascript/
  764.           var MAP = {
  765.                 '&': '&amp;',
  766.                 '<': '&lt;',
  767.                 '>': '&gt;',
  768.                 '"': '&quot;',
  769.                 "'": '&#39;'
  770.           };
  771.           var repl = function(c) { return MAP[c]; };
  772.           return function(s) {
  773.                 s = s.replace(/[&<>'"]/g, repl);
  774.                 return s;
  775.           };
  776.         })()
  777.         */
  778. });
  779.