omnomirc.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985
  1. /*
  2. OmnomIRC COPYRIGHT 2010,2011 Netham45
  3. OmnomIRC3 rewrite COPYRIGHT 2013 Nathaniel 'Eeems' van Diepen
  4. This file is part of OmnomIRC.
  5. OmnomIRC is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (at your option) any later version.
  9. OmnomIRC is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with OmnomIRC. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. (function(window,$,io,undefined){
  17. var $o = window.OmnomIRC = window.$o = function(){
  18. return 'Version: '+version;
  19. },
  20. event = function(msg,type){
  21. type=typeof type == 'undefined'?'event':type;
  22. switch(type){
  23. case 'ready':type='document_ready';break;
  24. }
  25. log('['+type.toUpperCase()+'] '+msg);
  26. },
  27. emit = window.emit = function(type,data){
  28. if($o.chat.connected()){
  29. socket.emit.apply(socket,arguments);
  30. }else{
  31. if(tabs.length > 0){
  32. $o.msg('Disconnected, cannot do anything');
  33. }
  34. }
  35. },
  36. noop = function(){},
  37. log = function(){
  38. console.log.apply(console,arguments);
  39. },
  40. exists = function(object){
  41. return typeof object != 'undefined';
  42. },
  43. prevent = function(e){
  44. e.stopImmediatePropagation();
  45. e.stopPropagation();
  46. e.preventDefault();
  47. return false;
  48. },
  49. selectedTab=0,
  50. settings = {
  51. colour: false,
  52. timestamp: 'exact',
  53. server: location.origin,
  54. autoconnect: true,
  55. autojoin: [
  56. '#omnimaga',
  57. '#omnimaga-fr',
  58. '#irp'
  59. ],
  60. scrollspeed: 100,
  61. theme: 'default',
  62. nick: 'User'
  63. },
  64. tabs = [],
  65. properties = {
  66. nick: 'User',
  67. sig: '',
  68. tabs: tabs,
  69. themes: [
  70. 'default'
  71. ]
  72. },
  73. commands = [
  74. { // names
  75. cmd: 'names',
  76. fn: function(args){
  77. emit('names',{
  78. name: $o.ui.tabs.current().name
  79. });
  80. }
  81. },
  82. { // me
  83. cmd: 'me',
  84. help: 'Say something in third person',
  85. fn: function(args){
  86. var i,ret='';
  87. for(i=1;i<args.length;i++){
  88. ret += ' '+args[i];
  89. }
  90. emit('message',{
  91. from: 0,
  92. message: properties.nick+' '+ret,
  93. room: $o.ui.tabs.current().name
  94. });
  95. }
  96. },
  97. { // connect
  98. cmd: 'connect',
  99. fn: function(){
  100. if(!$o.chat.connected()){
  101. $o.chat.connect();
  102. }
  103. }
  104. },
  105. { // disconnect
  106. cmd: 'disconnect',
  107. fn: function(){
  108. $o.disconnect();
  109. }
  110. },
  111. { // nick
  112. cmd: 'nick',
  113. fn: function(args){
  114. $o.set('nick',args[1]);
  115. }
  116. },
  117. { // help
  118. cmd: 'help',
  119. fn: function(args){
  120. if(typeof args[1] == 'undefined'){
  121. var m = 'Commands:',i;
  122. for(i in commands){
  123. m += ' '+commands[i].cmd;
  124. }
  125. $o.msg(m);
  126. }else{
  127. var i,cmd;
  128. for(i in commands){
  129. cmd = commands[i];
  130. if(cmd.cmd == args[1] && typeof cmd.help != 'undefined'){
  131. $o.msg('Command /'+cmd.cmd+': '+cmd.help);
  132. return;
  133. }
  134. }
  135. $o.send('/help');
  136. }
  137. }
  138. },
  139. { // open
  140. cmd: 'open',
  141. fn: function(args){
  142. $o.ui.tabs.add(args[1]);
  143. }
  144. },
  145. { // clear
  146. cmd: 'clear',
  147. fn: function(args){
  148. $o.ui.tabs.current().clear();
  149. }
  150. },
  151. { // close
  152. cmd: 'close',
  153. fn: function(args){
  154. if(args.length > 1){
  155. $o.ui.tabs.remove(args[1]);
  156. }else{
  157. $o.ui.tabs.remove(selectedTab);
  158. }
  159. }
  160. },
  161. { // tabs
  162. cmd: 'tabs',
  163. fn: function(args){
  164. $o.msg('tabs:');
  165. for(var i in tabs){
  166. $o.msg(' ['+i+'] '+tabs[i].name);
  167. }
  168. }
  169. }
  170. ],
  171. handles = [
  172. { // names
  173. on: 'names',
  174. fn: function(data){
  175. var tab = tabs[$o.ui.tabs.idForName(data.room)],
  176. users = tab.users,
  177. i;
  178. tab.users = data.names;
  179. if($o.ui.tabs.idForName(data.room) == selectedTab){
  180. $o.ui.render.users();
  181. }
  182. $(users).each(function(i,v){
  183. if(v != null){
  184. if(tab.users.indexOf(v.trim()) == -1){
  185. emit('echo',{
  186. room: $o.ui.tabs.current().name,
  187. message: v+' left the room',
  188. from: 0
  189. });
  190. }
  191. }
  192. });
  193. }
  194. },
  195. { // authorized
  196. on: 'authorized',
  197. fn: function(data){
  198. properties.nick = data.nick;
  199. for(var i in settings.autojoin){
  200. emit('join',{
  201. name: settings.autojoin[i]
  202. });
  203. }
  204. }
  205. },
  206. { // join
  207. on: 'join',
  208. fn: function(data){
  209. event('joined '+data.name);
  210. var flag = tabs.length == 0;
  211. $o.ui.tabs.add(data.name);
  212. if(flag){
  213. $o.ui.tabs.select(0);
  214. }
  215. }
  216. },
  217. { // reconnect
  218. on: 'reconnect',
  219. fn: function(data){
  220. event('reconnected');
  221. properties.connected = true;
  222. $o.chat.auth();
  223. emit('echo',{
  224. room: $o.ui.tabs.current().name,
  225. from: 0,
  226. message: 'reconnected'
  227. });
  228. }
  229. },
  230. { // connect
  231. on: 'connect',
  232. fn: function(data){
  233. event('connected');
  234. properties.connected = true;
  235. $o.chat.auth();
  236. emit('echo',{
  237. room: $o.ui.tabs.current().name,
  238. from: 0,
  239. message: 'connected'
  240. });
  241. }
  242. },
  243. { // disconnect
  244. on: 'disconnect',
  245. fn: function(data){
  246. event('disconnected');
  247. properties.connected = false;
  248. $o.msg('* disconnected');
  249. }
  250. },
  251. { // message
  252. on: 'message',
  253. fn: function(data){
  254. event('recieved message');
  255. var date = new Date(),
  256. string,
  257. time = date.getTime(),
  258. child,
  259. i,
  260. msg = function(msg){
  261. string = '<span class="cell date_cell">[<abbr class="date date_'+time+'" title="'+date.toISOString()+'"></abbr>]</span>';
  262. child = $('<li>').html(string+'<span class="cell">'+
  263. msg
  264. .htmlentities()
  265. .replace(
  266. /(https?:\/\/(([-\w\.]+)+(:\d+)?(\/([\w/_\.]*(\?\S+)?)?)?))/g,
  267. "<a href=\"$1\" title=\"\">$1</a>"
  268. )
  269. +'</span>');
  270. $o.msg({html:child},data.room);
  271. };
  272. if(data.from != 0){
  273. msg(' <'+data.from+'> '+data.message);
  274. }else{
  275. msg(' * '+data.message);
  276. }
  277. abbrDate('abbr.date_'+time);
  278. if(settings.timestamp == ''){
  279. $('.date_cell').css('visibility','hidden');
  280. }else{
  281. $('.date_cell').css('visibility','visible');
  282. }
  283. }
  284. }
  285. ],
  286. hooks = [
  287. { // load - style
  288. type: 'style',
  289. hook: 'load',
  290. fn: function(){
  291. }
  292. }
  293. ],
  294. runHook = function(name){
  295. var i,hook;
  296. for(i in hooks){
  297. hook = hooks[i];
  298. if(hook.hook == name){
  299. with({
  300. // Sandbox
  301. }){
  302. hook.fn();
  303. }
  304. }
  305. }
  306. },
  307. version = '3.0',
  308. abbrDate = function(selector){
  309. if(settings.timestamp == 'fuzzy'){
  310. $(selector).timeago();
  311. }else{
  312. $(selector).each(function(){
  313. var timestamp = settings.timestamp,
  314. i,
  315. text='',
  316. date = new Date($(this).attr('title'));
  317. if(timestamp == 'exact'){
  318. timestamp = 'H:m:s t';
  319. }
  320. for(i=0;i<timestamp.length;i++){
  321. switch(timestamp[i]){
  322. case 'H':text+=((date.getHours()+11)%12)+1;break;
  323. case 'h':text+=date.getHours();break;
  324. case 'm':text+=(date.getMinutes()>9?'':'0')+date.getMinutes();break;
  325. case 's':text+=(date.getSeconds()>9?'':'0')+date.getSeconds();break;
  326. case 't':text+=(date.getHours()>11)?'pm':'am';break;
  327. default:text+=timestamp[i];
  328. }
  329. }
  330. $(this).text(text);
  331. }).timeago('dispose');
  332. }
  333. },
  334. socket,$i,$s,$h,$cl,$tl,hht;
  335. $.extend($o,{
  336. version: function(){
  337. return version;
  338. },
  339. register: {
  340. theme: function(name){
  341. if(-1==$.inArray(properties.themes,name)){
  342. properties.themes.push(name);
  343. return true;
  344. }
  345. return false;
  346. },
  347. command: function(name,fn,help){
  348. if(-1==$.inArray(commands,name)){
  349. var o = {
  350. cmd: name,
  351. fn: fn
  352. };
  353. if(typeof help != 'undefined'){
  354. o.help = help;
  355. }
  356. commands.push(o);
  357. return true;
  358. }
  359. return false;
  360. },
  361. plugin: function(){
  362. // STUB
  363. },
  364. setting: function(name,defaultVal,validate,values){
  365. // STUB
  366. },
  367. hook: function(event,fn){
  368. }
  369. },
  370. hook: function(event,fn){
  371. $o.register.hook(event,fn);
  372. },
  373. ui: {
  374. render: {
  375. settings: function(){
  376. var name,setting,frag = document.createDocumentFragment(),item;
  377. for(name in settings){
  378. setting = $o.get(name,true);
  379. switch(setting.type){
  380. case 'select':
  381. item = $('<select>')
  382. .attr('id','setting_'+name)
  383. .change(function(){
  384. $o.set(this.id.substr(8),$(this).find(':selected').text(),false);
  385. });
  386. for(var i in setting.values){
  387. item.append(
  388. $('<option>')
  389. .text(setting.values[i])
  390. );
  391. }
  392. item.find(':contains('+setting.val+')').attr('selected','selected');
  393. break;
  394. case 'array':
  395. item = $('<input>')
  396. .attr({
  397. type: 'text',
  398. id: 'setting_'+name
  399. })
  400. .val(setting.val)
  401. .change(function(){
  402. $o.set(this.id.substr(8),$(this).val().split(','),false);
  403. });
  404. break;
  405. case 'boolean':
  406. item = $('<input>')
  407. .attr({
  408. type: 'checkbox',
  409. id: 'setting_'+name
  410. })
  411. .change(function(){
  412. $o.set(this.id.substr(8),$(this).is(':checked'),false);
  413. });
  414. if(setting.val){
  415. item.attr('checked','checked');
  416. }
  417. break;
  418. case 'number':
  419. case 'string':default:
  420. item = $('<input>')
  421. .attr({
  422. type: 'text',
  423. id: 'setting_'+name
  424. })
  425. .val(setting.val)
  426. .change(function(){
  427. $o.set(this.id.substr(8),$(this).val(),false);
  428. });
  429. }
  430. $(frag).append(
  431. $('<li>')
  432. .addClass('row')
  433. .append(
  434. $('<span>')
  435. .text(name)
  436. .addClass('cell')
  437. )
  438. .append(
  439. $('<span>')
  440. .append(item)
  441. .addClass('cell')
  442. )
  443. );
  444. }
  445. $('#settings-list').html(frag);
  446. },
  447. users: function(){
  448. event('Rendering userlist');
  449. var $ul = $('#user-list').html(''),
  450. i,
  451. names = $o.ui.tabs.current().users;
  452. for(i in names){
  453. $ul.append(
  454. $('<li>').text(names[i])
  455. );
  456. }
  457. },
  458. tab: function(){
  459. $cl.html($($o.ui.tabs.current().body).clone());
  460. },
  461. tablist: function(){
  462. $tl.html('');
  463. var i,tab;
  464. for(i in tabs){
  465. tab = $o.ui.tabs.obj(i);
  466. if(i==selectedTab){
  467. tab.addClass('clicked');
  468. $('#title').text(tabs[i].name);
  469. $('#topic').text(tabs[i].topic);
  470. }
  471. $tl.append(tab);
  472. }
  473. if($tl.get(0).scrollHeight-20 != $tl.scrollTop()){
  474. $('#tabs-scroll-right').removeClass('disabled');
  475. }
  476. if($tl.scrollTop() != 0){
  477. $('#tabs-scroll-left').removeClass('disabled');
  478. }
  479. }
  480. },
  481. tabs: {
  482. add: function(name){
  483. event('Tab added: '+name);
  484. if(!(function(){
  485. for(var i in tabs){
  486. if(name==tabs[i].name){
  487. return true;
  488. }
  489. }
  490. return false;
  491. })()){
  492. var scroll = $.localStorage('tabs'),
  493. i,
  494. frag = document.createDocumentFragment(),
  495. id = tabs.length;
  496. for(i in scroll){
  497. if(scroll[i].name == name){
  498. scroll[i].body = $(scroll[i].body).slice(-10);
  499. $(frag)
  500. .append(scroll[i].body)
  501. .append(
  502. $('<li>').html('<span class="to_remove">-- loaded old scrollback for '+scroll[i].date+' --</span>')
  503. )
  504. .children()
  505. .children('.remove')
  506. .remove();
  507. $(frag)
  508. .children()
  509. .children('.to_remove')
  510. .removeClass('to_remove')
  511. .addClass('remove');
  512. event('loading old tab scrollback for '+name+' last saved +'+scroll[i].date);
  513. }
  514. }
  515. tabs.push({
  516. name: name,
  517. body: frag,
  518. date: new Date(),
  519. send: function(msg){
  520. $o.chat.send(msg,$o.ui.tabs.tab(id).name);
  521. },
  522. close: function(){
  523. $o.ui.tabs.remove(id);
  524. },
  525. users: [],
  526. names: function(){
  527. emit('names',{
  528. name: $o.ui.tabs.tab(id).name
  529. });
  530. },
  531. select: function(){
  532. $o.ui.tabs.select(id);
  533. },
  534. clear: function(){
  535. $cl.html('');
  536. $o.ui.tabs.tab(id).body = document.createDocumentFragment();
  537. emit('echo',{
  538. room: $o.ui.tabs.tab(id).name,
  539. message: 'messages cleared',
  540. from: 0
  541. });
  542. }
  543. });
  544. $tl.append($o.ui.tabs.obj(id));
  545. $o.ui.render.tablist();
  546. $o.ui.render.users();
  547. }else{
  548. event('Attempted to add an existing tab');
  549. }
  550. },
  551. remove: function(name){
  552. if(typeof name == 'number'){
  553. name = tabs[name].name;
  554. }
  555. for(var id=0;id<tabs.length;id++){
  556. if($o.ui.tabs.tab(id).name == name){
  557. event('Tab removed: '+$o.ui.tabs.tab(id).name);
  558. emit('part',{
  559. name: $o.ui.tabs.tab(id).name
  560. });
  561. tabs.splice(id,1);
  562. if(selectedTab==id&&selectedTab>0){
  563. selectedTab--;
  564. }
  565. break;
  566. }
  567. }
  568. $o.ui.render.tablist();
  569. $cl.html($o.ui.tabs.current().body);
  570. $o.ui.render.users();
  571. },
  572. selected: function(){
  573. return selectedTab;
  574. },
  575. idForName: function(name){
  576. for(var i in tabs){
  577. if(tabs[i].name == name){
  578. return i;
  579. }
  580. }
  581. return false;
  582. },
  583. tab: function(id){
  584. return typeof tabs[id] == 'undefined'?false:tabs[id];
  585. },
  586. dom: function(id){
  587. return typeof tabs[id] == 'undefined'?false:tabs[id].body;
  588. },
  589. obj: function(id){
  590. if(typeof id !== 'undefined'){
  591. return $('<div>')
  592. .addClass('tab')
  593. .text($o.ui.tabs.tab(id).name)
  594. .mouseup(function(e){
  595. switch(e.which){
  596. case 1: // RMB
  597. if($(this).data('id')!=selectedTab){
  598. $o.ui.tabs.select($(this).data('id'));
  599. return prevent(e);
  600. }
  601. break;
  602. case 2: // MMB
  603. $(this).children('span.close-button').click();
  604. return prevent(e);
  605. break;
  606. case 3: // LMB
  607. return prevent(e);
  608. break;
  609. default:
  610. return prevent(e);
  611. }
  612. })
  613. .append(
  614. $('<span>')
  615. .addClass('close-button')
  616. .click(function(){
  617. $o.ui.tabs.remove(id);
  618. return false;
  619. })
  620. .css({
  621. 'position': 'absolute',
  622. 'background-color': 'inherit',
  623. 'top': 0,
  624. 'right': 0
  625. })
  626. .html('&times;')
  627. )
  628. .data('id',id);
  629. }
  630. },
  631. select: function(id){
  632. event(id+' '+$o.ui.tabs.tab(id).name,'tab_select');
  633. if(id<tabs.length&&id>=0){
  634. selectedTab=id;
  635. }
  636. $tl.children('.clicked').removeClass('clicked');
  637. $($tl.children().get(id)).addClass('clicked');
  638. $('#title').text($o.ui.tabs.tab(id).name);
  639. $('#topic').text($o.ui.tabs.tab(id).topic);
  640. $cl.html($($o.ui.tabs.tab(id).body).clone());
  641. abbrDate('abbr.date');
  642. $o.ui.render.users();
  643. setTimeout(function scrollContent(){
  644. if($c.scrollTop() < $c[0].scrollHeight){
  645. $c.scrollTop($c.scrollTop()+1);
  646. setTimeout(scrollContent,settings.scrollspeed);
  647. }else{
  648. event('scrolling stopped');
  649. }
  650. },settings.scrollspeed);
  651. },
  652. current: function(){
  653. if(tabs.length > 0 && tabs.length > selectedTab){
  654. return tabs[selectedTab];
  655. }else{
  656. return {
  657. name: '',
  658. body: document.createDocumentFragment(),
  659. date: new Date(),
  660. send: noop,
  661. close: noop,
  662. users: [],
  663. names: noop,
  664. select: noop,
  665. clear: noop
  666. }
  667. }
  668. }
  669. },
  670. },
  671. chat: {
  672. connect: function(server){
  673. if($o.chat.connected()){
  674. $o.disconnect();
  675. }
  676. if(typeof server == 'undefined'){
  677. server = settings.server;
  678. }
  679. socket = window.socket = io.connect(server);
  680. for(var i in handles){
  681. socket.on(handles[i].on,handles[i].fn);
  682. }
  683. $o.chat.auth();
  684. },
  685. disconnect: function(){
  686. if($o.chat.connected()){
  687. socket.disconnect();
  688. }
  689. },
  690. connected: function(){
  691. return typeof socket == 'undefined'?false:properties.connected;
  692. },
  693. send: function(msg,room){
  694. if(typeof room == 'undefined'){
  695. room = $o.ui.tabs.current().name;
  696. }
  697. if(msg !== ''){
  698. if(msg[0] == '/' && msg[1] != '/'){
  699. var args = msg.split(' '),
  700. cmd = args[0].substr(1),
  701. i;
  702. event(msg,'command');
  703. for(i in commands){
  704. if(commands[i].cmd == cmd){
  705. commands[i].fn(args);
  706. return;
  707. }
  708. }
  709. $o.msg(cmd+' is not a valid command.');
  710. }else{
  711. event(msg,'send');
  712. emit('message',{
  713. message: msg,
  714. room: room,
  715. from: properties.nick
  716. });
  717. }
  718. }
  719. },
  720. auth: function(){
  721. if(settings.nick == ''){
  722. $o.set('nick','User');
  723. return;
  724. }
  725. emit('auth',{
  726. nick: settings.nick
  727. // TODO - send authorization info
  728. });
  729. }
  730. },
  731. get: function(name,formatted){
  732. if(typeof formatted == 'undefined'){
  733. return exists(settings[name])?settings[name]:false;
  734. }else{
  735. var val = $o.get(name),
  736. type,
  737. values = false;
  738. switch(name){
  739. case 'theme':
  740. type = 'select';
  741. values = properties.themes;
  742. break;
  743. case 'autojoin':type = 'array';break;
  744. case 'timestamp':type = 'string';break;
  745. default:
  746. type = typeof val;
  747. }
  748. return {
  749. type: type,
  750. val: val,
  751. values: values,
  752. name: name
  753. };
  754. }
  755. },
  756. set: function(name,value,render){
  757. if(exists(settings[name])){
  758. settings[name] = value;
  759. $.localStorage('settings',JSON.stringify(settings));
  760. switch(name){
  761. case 'timestamp':
  762. abbrDate('abbr.date');
  763. if(settings.timestamp == ''){
  764. $('.date_cell').css('visibility','hidden');
  765. }else{
  766. $('.date_cell').css('visibility','visible');
  767. }
  768. break;
  769. case 'nick':
  770. $o.chat.auth();
  771. break;
  772. }
  773. if(typeof render == 'undefined'){
  774. $o.ui.render.settings();
  775. }
  776. return true;
  777. }else{
  778. return false;
  779. }
  780. },
  781. prop: function(name){
  782. return exists(properties[name])?properties[name]:null;
  783. },
  784. send: function(msg){
  785. $o.chat.send(msg);
  786. },
  787. msg: function(msg,tabName){
  788. var frag;
  789. if(typeof tabName == 'undefined' || tabName == $o.ui.tabs.current().name){
  790. frag = document.createDocumentFragment();
  791. }else{
  792. frag = tabs[$o.ui.tabs.idForName(tabName)].body;
  793. }
  794. switch(typeof msg){
  795. case 'string':
  796. $(frag).append($('<li>').html(msg.htmlentities()));
  797. break;
  798. case 'object':
  799. if(typeof msg.html == 'undefined'){
  800. $(frag).append($('<li>').html('&lt;'+msg.user+'&gt;&nbsp;'+msg.text.htmlentities()));
  801. }else{
  802. $(frag).append(msg.html);
  803. }
  804. break;
  805. }
  806. if(tabs.length > 0){
  807. $(tabs[$o.ui.tabs.idForName(tabName) || selectedTab].body).append(frag);
  808. }
  809. var scroll = [],i,html;
  810. for(i in tabs){
  811. html = '';
  812. $(tabs[i].body).children().each(function(){
  813. html += this.outerHTML;
  814. });
  815. scroll.push({
  816. name: tabs[i].name,
  817. body: html,
  818. date: new Date().toString()
  819. });
  820. }
  821. $.localStorage('tabs',scroll);
  822. if(typeof tabName == 'undefined' || tabName == $o.ui.tabs.current().name){
  823. $o.ui.tabs.select(selectedTab);
  824. }
  825. },
  826. event: function(event_name,message){
  827. event(message,event_name);
  828. }
  829. });
  830. String.prototype.htmlentities = function(){
  831. return this
  832. .replace(/&/g, '&amp;')
  833. .replace(/</g, '&lt;')
  834. .replace(/>/g, '&gt;')
  835. .replace(/\n/g,'<br/>')
  836. .replace(/\t/g, '&nbsp;&nbsp;&nbsp;&nbsp;')
  837. .replace(/\s/g, '&nbsp;')
  838. .replace(/"/g, '&quot;');
  839. };
  840. $(document).ready(function(){
  841. $.extend(settings,$.parseJSON($.localStorage('settings')));
  842. $.localStorage('settings',JSON.stringify(settings));
  843. $i = $('#input');
  844. $s = $('#send');
  845. $cl = $('#content-list');
  846. $c = $('#content');
  847. $tl = $('#tabs-list');
  848. $h = $('#head');
  849. $s.click(function(){
  850. if(!$s.hasClass('clicked')){
  851. $s.addClass('clicked');
  852. setTimeout(function(){
  853. $s.removeClass('clicked');
  854. },500);
  855. }
  856. $o.send($i.val());
  857. $i.val('');
  858. });
  859. $i.keypress(function(e){
  860. if(e.keyCode == 13){
  861. if(!$s.hasClass('clicked')){
  862. $s.addClass('clicked');
  863. setTimeout(function(){
  864. $s.removeClass('clicked');
  865. },500);
  866. }
  867. $o.send($i.val());
  868. $i.val('');
  869. }
  870. });
  871. $('#settings, #users').click(function(){
  872. $(this).addClass('open');
  873. $(this).children('.close-button').show();
  874. }).hover(function(){
  875. $(this).addClass('hovered');
  876. },function(){
  877. $(this).removeClass('hovered');
  878. }).children('.close-button').click(function(){
  879. $(this).parent().removeClass('open');
  880. $(this).hide();
  881. return false;
  882. }).hide();
  883. $('#users').hoverIntent({
  884. out: function(){
  885. $(this).removeClass('open');
  886. $(this).children('.close-button').hide();
  887. },
  888. timeout: 1000
  889. });
  890. $('#content').click(function(){
  891. $('#settings, #users, #head').removeClass('hovered').removeClass('open');
  892. $('#settings, #users').children('.close-button').hide()
  893. });
  894. $('.unselectable').attr('unselectable','on');
  895. $.contextMenu({
  896. selector: 'div.tab',
  897. items: {
  898. add: {
  899. name: 'New Tab',
  900. icon: 'add',
  901. callback: function(){
  902. $(this).contextMenu('hide');
  903. $o.ui.tabs.add(prompt('Channel'));
  904. }
  905. },
  906. s1: '',
  907. close: {
  908. name: 'Close',
  909. icon: 'delete',
  910. callback: function(){
  911. $(this).contextMenu('hide');
  912. $o.ui.tabs.remove($(this).data('id'));
  913. }
  914. }
  915. },
  916. zIndex: 99999,
  917. trigger: 'right'
  918. });
  919. $.contextMenu({
  920. selector: '#tabs-list',
  921. items: {
  922. add: {
  923. name: 'New Tab',
  924. icon: 'add',
  925. callback: function(){
  926. $(this).contextMenu('hide');
  927. $o.ui.tabs.add(prompt('channel'));
  928. }
  929. }
  930. },
  931. zIndex: 99999,
  932. trigger: 'right'
  933. });
  934. $('#tabs-scroll-right').click(function(){
  935. event('scroll right');
  936. $tl.scrollTop(($tl.scrollTop()||0)+20);
  937. if($tl.get(0).scrollHeight-20 == $tl.scrollTop()){
  938. $('#tabs-scroll-right').addClass('disabled');
  939. }
  940. $('#tabs-scroll-left').removeClass('disabled');
  941. });
  942. $('#tabs-scroll-left').click(function(){
  943. event('scroll left');
  944. $tl.scrollTop(($tl.scrollTop()||0)-20);
  945. if($tl.scrollTop() == 0){
  946. $('#tabs-scroll-left').addClass('disabled');
  947. }
  948. $('#tabs-scroll-right').removeClass('disabled');
  949. });
  950. (function scrollup(){
  951. $('#tabs-scroll-left').click();
  952. if($tl.scrollTop() != 0){
  953. setTimeout(scrollup,10);
  954. }
  955. })();
  956. event('Date '+new Date,'ready');
  957. $h.addClass('hovered');
  958. setTimeout(function(){
  959. $h.removeClass('hovered');
  960. },1000);
  961. $o.ui.render.settings();
  962. if(settings.autoconnect){
  963. $o.chat.connect();
  964. }
  965. });
  966. delete window.io;
  967. })(window,jQuery,io);
  968. if (!Date.prototype.toISOString) {
  969. Date.prototype.toISOString = function() {
  970. function pad(n) { return n < 10 ? '0' + n : n }
  971. return this.getUTCFullYear() + '-'
  972. + pad(this.getUTCMonth() + 1) + '-'
  973. + pad(this.getUTCDate()) + 'T'
  974. + pad(this.getUTCHours()) + ':'
  975. + pad(this.getUTCMinutes()) + ':'
  976. + pad(this.getUTCSeconds()) + 'Z';
  977. };
  978. }