omnomirc.js 26 KB

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