omnomirc.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811
  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: '+$o.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. log = function(){
  28. console.log.apply(console,arguments);
  29. },
  30. exists = function(object){
  31. return typeof object != 'undefined';
  32. },
  33. prevent = function(e){
  34. e.stopImmediatePropagation();
  35. e.stopPropagation();
  36. e.preventDefault();
  37. return false;
  38. },
  39. selectedTab=0,
  40. settings = {
  41. colour: false,
  42. timestamp: 'exact',
  43. server: location.origin,
  44. autojoin: [
  45. '#omnimaga',
  46. '#omnimaga-fr',
  47. '#irp'
  48. ],
  49. scrollspeed: 100,
  50. theme: 'default'
  51. },
  52. tabs = [],
  53. properties = {
  54. nick: 'User',
  55. sig: '',
  56. tabs: tabs,
  57. themes: [
  58. 'default'
  59. ]
  60. },
  61. commands = [
  62. {
  63. cmd: 'names',
  64. fn: function(args){
  65. socket.emit('names',{
  66. name: tabs[selectedTab].name
  67. });
  68. }
  69. },
  70. {
  71. cmd: 'me',
  72. help: 'Say something in third person',
  73. fn: function(args){
  74. var i,ret='';
  75. for(i=1;i<args.length;i++){
  76. ret += args[i];
  77. }
  78. socket.emit('message',{
  79. from: 0,
  80. message: properties.nick+' '+ret,
  81. room: tabs[selectedTab].name
  82. });
  83. }
  84. },
  85. {
  86. cmd: 'nick',
  87. fn: function(args){
  88. properties.nick = args[1];
  89. $o.auth();
  90. }
  91. },
  92. {
  93. cmd: 'help',
  94. fn: function(args){
  95. if(typeof args[1] == 'undefined'){
  96. var m = 'Commands:',i;
  97. for(i in commands){
  98. m += ' '+commands[i].cmd;
  99. }
  100. $o.msg(m);
  101. }else{
  102. var i,cmd;
  103. for(i in commands){
  104. cmd = commands[i];
  105. if(cmd.cmd == args[1] && typeof cmd.help != 'undefined'){
  106. $o.msg('Command /'+cmd.cmd+': '+cmd.help);
  107. return;
  108. }
  109. }
  110. $o.send('/help');
  111. }
  112. }
  113. },
  114. {
  115. cmd: 'open',
  116. fn: function(args){
  117. tabs.push({
  118. name: args[1],
  119. title: args[2],
  120. topic: 'Topic for '+args[2]
  121. });
  122. $o.refreshTabs();
  123. }
  124. },
  125. { // clear
  126. cmd: 'clear',
  127. fn: function(args){
  128. $cl.html('');
  129. tabs[selectedTab].body = document.createDocumentFragment();
  130. socket.emit('echo',{
  131. room: tabs[selectedTab].name,
  132. message: 'messages cleared',
  133. name: 0
  134. });
  135. }
  136. },
  137. { // close
  138. cmd: 'close',
  139. fn: function(args){
  140. if(args.length > 1){
  141. $o.removeTab(args[1]);
  142. }else{
  143. $o.removeTab(selectedTab);
  144. }
  145. }
  146. },
  147. { // tabs
  148. cmd: 'tabs',
  149. fn: function(args){
  150. $o.msg('tabs:');
  151. for(var i in tabs){
  152. $o.msg(' ['+i+'] '+tabs[i].name);
  153. }
  154. }
  155. }
  156. ],
  157. handles = [
  158. {
  159. on: 'names',
  160. fn: function(data){
  161. tabs[$o.tabIdForName(data.room)].names = data.names;
  162. if($o.tabIdForName(data.room) == selectedTab){
  163. $o.renderUsers();
  164. }
  165. }
  166. },
  167. {
  168. on: 'authorized',
  169. fn: function(data){
  170. properties.nick = data.nick;
  171. for(var i in settings.autojoin){
  172. socket.emit('join',{
  173. name: settings.autojoin[i]
  174. });
  175. }
  176. }
  177. },
  178. {
  179. on: 'join',
  180. fn: function(data){
  181. event('joined '+data.name);
  182. var flag = tabs.length == 0;
  183. $o.addTab(data.name,data.title);
  184. if(flag){
  185. $o.selectTab(0);
  186. }
  187. }
  188. },
  189. {
  190. on: 'reconnect',
  191. fn: function(data){
  192. event('reconnected');
  193. $o.auth();
  194. }
  195. },
  196. {
  197. on: 'message',
  198. fn: function(data){
  199. event('recieved message');
  200. var date = new Date(),
  201. string,
  202. time = date.getTime(),
  203. child,
  204. i,
  205. msg = function(msg){
  206. string = '<span class="cell date_cell">[<abbr class="date date_'+time+'" title="'+date.toISOString()+'"></abbr>]</span>';
  207. child = $('<li>').html(string+'<span class="cell">'+msg.htmlentities().replace(
  208. /(https?:\/\/(([-\w\.]+)+(:\d+)?(\/([\w/_\.]*(\?\S+)?)?)?))/g,
  209. "<a href=\"$1\" title=\"\">$1</a>"
  210. )+'</span>');
  211. $o.msg({html:child},data.room);
  212. };
  213. if(data.from != 0){
  214. msg(' <'+data.from+'> '+data.message);
  215. }else{
  216. msg(' * '+data.message);
  217. }
  218. abbrDate('abbr.date_'+time);
  219. if(settings.timestamp == ''){
  220. $('.date_cell').css('visibility','hidden');
  221. }else{
  222. $('.date_cell').css('visibility','visible');
  223. }
  224. }
  225. }
  226. ],
  227. hooks = [
  228. {
  229. type: 'style',
  230. hook: 'load',
  231. fn: function(){
  232. }
  233. }
  234. ],
  235. abbrDate = function(selector){
  236. if(settings.timestamp == 'fuzzy'){
  237. $(selector).timeago();
  238. }else{
  239. $(selector).each(function(){
  240. var timestamp = settings.timestamp,
  241. i,
  242. text='',
  243. date = new Date($(this).attr('title'));
  244. if(timestamp == 'exact'){
  245. timestamp = 'H:m:s t';
  246. }
  247. for(i=0;i<timestamp.length;i++){
  248. switch(timestamp[i]){
  249. case 'H':text+=((date.getHours()+11)%12)+1;break;
  250. case 'h':text+=date.getHours();break;
  251. case 'm':text+=(date.getMinutes()>9?'':'0')+date.getMinutes();break;
  252. case 's':text+=(date.getSeconds()>9?'':'0')+date.getSeconds();break;
  253. case 't':text+=(date.getHours()>11)?'pm':'am';break;
  254. default:text+=timestamp[i];
  255. }
  256. }
  257. $(this).text(text);
  258. }).timeago('dispose');
  259. }
  260. },
  261. socket,$i,$s,$h,$cl,$tl,hht;
  262. $.extend($o,{
  263. version: '3.0',
  264. register: {
  265. theme: function(name){
  266. if(-1==$.inArray(properties.themes,name)){
  267. properties.themes.push(name);
  268. return true;
  269. }
  270. return false;
  271. },
  272. command: function(name,fn,help){
  273. if(-1==$.inArray(commands,name)){
  274. var o = {
  275. cmd: name,
  276. fn: fn
  277. };
  278. if(typeof help != 'undefined'){
  279. o.help = help;
  280. }
  281. commands.push(o);
  282. return true;
  283. }
  284. return false;
  285. }
  286. },
  287. connect: function(server){
  288. if($o.connected()){
  289. socket.disconnect();
  290. socket = undefined;
  291. }
  292. if(typeof server == 'undefined'){
  293. server = settings.server;
  294. }
  295. socket = io.connect(server);
  296. for(var i in handles){
  297. socket.on(handles[i].on,handles[i].fn);
  298. }
  299. $o.auth();
  300. },
  301. auth: function(){
  302. socket.emit('auth',{
  303. nick: properties.nick
  304. // TODO - send authorization info
  305. });
  306. },
  307. connected: function(){
  308. return typeof socket != 'undefined';
  309. },
  310. get: function(name,formatted){
  311. if(typeof formatted == 'undefined'){
  312. return exists(settings[name])?settings[name]:false;
  313. }else{
  314. var val = $o.get(name),
  315. type,
  316. values = false;
  317. switch(name){
  318. case 'theme':
  319. type = 'select';
  320. values = properties.themes;
  321. break;
  322. case 'autojoin':type = 'array';break;
  323. case 'timestamp':type = 'string';break;
  324. default:
  325. type = typeof val;
  326. }
  327. return {
  328. type: type,
  329. val: val,
  330. values: values,
  331. name: name
  332. };
  333. }
  334. },
  335. set: function(name,value,render){
  336. if(exists(settings[name])){
  337. settings[name] = value;
  338. $.localStorage('settings',JSON.stringify(settings));
  339. switch(name){
  340. case 'timestamp':
  341. abbrDate('abbr.date');
  342. if(settings.timestamp == ''){
  343. $('.date_cell').css('visibility','hidden');
  344. }else{
  345. $('.date_cell').css('visibility','visible');
  346. }
  347. break;
  348. }
  349. if(typeof render == 'undefined'){
  350. $o.renderSettings();
  351. }
  352. return true;
  353. }else{
  354. return false;
  355. }
  356. },
  357. renderSettings: function(){
  358. var name,setting,frag = document.createDocumentFragment(),item;
  359. for(name in settings){
  360. setting = $o.get(name,true);
  361. switch(setting.type){
  362. case 'array':
  363. item = $('<input>')
  364. .attr({
  365. type: 'text',
  366. id: 'setting_'+name
  367. })
  368. .val(setting.val)
  369. .change(function(){
  370. $o.set(this.id.substr(8),$(this).val().split(','),false);
  371. });
  372. break;
  373. case 'boolean':
  374. item = $('<input>')
  375. .attr({
  376. type: 'checkbox',
  377. id: 'setting_'+name
  378. })
  379. .change(function(){
  380. $o.set(this.id.substr(8),$(this).is(':checked'),false);
  381. });
  382. if(setting.val){
  383. item.attr('checked','checked');
  384. }
  385. break;
  386. case 'number':
  387. case 'string':default:
  388. item = $('<input>')
  389. .attr({
  390. type: 'text',
  391. id: 'setting_'+name
  392. })
  393. .val(setting.val)
  394. .change(function(){
  395. $o.set(this.id.substr(8),$(this).val(),false);
  396. });
  397. }
  398. $(frag).append(
  399. $('<li>')
  400. .addClass('row')
  401. .append(
  402. $('<span>')
  403. .text(name)
  404. .addClass('cell')
  405. )
  406. .append(
  407. $('<span>')
  408. .append(item)
  409. .addClass('cell')
  410. )
  411. );
  412. }
  413. $('#settings-list').html(frag);
  414. },
  415. renderUsers: function(){
  416. event('Rendering userlist');
  417. var $ul = $('#user-list').html(''),
  418. i,
  419. names = tabs[selectedTab].names;
  420. for(i in names){
  421. $ul.append(
  422. $('<li>').text(names[i])
  423. );
  424. }
  425. },
  426. selectedTab: function(){
  427. return selectedTab;
  428. },
  429. prop: function(name){
  430. return exists(properties[name])?properties[name]:null;
  431. },
  432. send: function(msg){
  433. if(msg !== ''){
  434. if(msg[0] == '/' && msg[1] != '/'){
  435. var args = msg.split(' '),
  436. cmd = args[0].substr(1),
  437. i;
  438. event(msg,'command');
  439. for(i in commands){
  440. if(commands[i].cmd == cmd){
  441. commands[i].fn(args);
  442. return;
  443. }
  444. }
  445. $o.msg(cmd+' is not a valid command.');
  446. }else{
  447. event(msg,'send');
  448. socket.emit('message',{
  449. message: msg,
  450. room: tabs[selectedTab].name,
  451. from: properties.nick
  452. });
  453. }
  454. }
  455. },
  456. msg: function(msg,tabName){
  457. var frag;
  458. if(typeof tabName == 'undefined' || tabName == tabs[selectedTab].name){
  459. frag = document.createDocumentFragment();
  460. }else{
  461. frag = tabs[$o.tabIdForName(tabName)].body;
  462. }
  463. switch(typeof msg){
  464. case 'string':
  465. $(frag).append($('<li>').html(msg.htmlentities()));
  466. break;
  467. case 'object':
  468. if(typeof msg.html == 'undefined'){
  469. $(frag).append($('<li>').html('&lt;'+msg.user+'&gt;&nbsp;'+msg.text.htmlentities()));
  470. }else{
  471. $(frag).append(msg.html);
  472. }
  473. break;
  474. }
  475. $(tabs[$o.tabIdForName(tabName) || selectedTab].body).append(frag);
  476. var scroll = [],i,html;
  477. for(i in tabs){
  478. html = '';
  479. $(tabs[i].body).children().each(function(){
  480. html += this.outerHTML;
  481. });
  482. scroll.push({
  483. name: tabs[i].name,
  484. body: html
  485. });
  486. }
  487. $.localStorage('tabs',scroll);
  488. if(typeof tabName == 'undefined' || tabName == tabs[selectedTab].name){
  489. $o.selectTab(selectedTab);
  490. }
  491. },
  492. event: function(event_name,message){
  493. event(message,event_name);
  494. },
  495. selectTab: function(id){
  496. event(id+' '+tabs[id].name,'tab_select');
  497. if(id<tabs.length&&id>=0){
  498. selectedTab=id;
  499. }
  500. $tl.children('.clicked').removeClass('clicked');
  501. $($tl.children().get(id)).addClass('clicked');
  502. $('#title').text(tabs[id].title);
  503. $('#topic').text(tabs[id].topic);
  504. $cl.html($(tabs[id].body).clone());
  505. abbrDate('abbr.date');
  506. $o.renderUsers();
  507. setTimeout(function scrollContent(){
  508. if($cl.scrollTop() < $cl[0].scrollHeight){
  509. $cl.scrollTop($cl.scrollTop()+1);
  510. setTimeout(scrollContent,settings.scrollspeed);
  511. }
  512. },settings.scrollspeed);
  513. },
  514. tabIdForName: function(name){
  515. for(var i in tabs){
  516. if(tabs[i].name == name){
  517. return i;
  518. }
  519. }
  520. return false;
  521. },
  522. tabDOM: function(id){
  523. return tabs[id].body;
  524. },
  525. addTab: function(name,title){
  526. event('Tab added: '+name+' with title '+title);
  527. if(!(function(){
  528. for(var i in tabs){
  529. if(name==tabs[i].name){
  530. return true;
  531. }
  532. }
  533. return false;
  534. })()){
  535. var scroll = $.localStorage('tabs'),
  536. i,
  537. frag = document.createDocumentFragment();
  538. for(i in scroll){
  539. if(scroll[i].name == name){
  540. $(frag)
  541. .append(scroll[i].body)
  542. .append(
  543. $('<li>').html('<span class="to_remove">-- loaded old scrollback --</span>')
  544. )
  545. .children()
  546. .children('.remove')
  547. .remove();
  548. $(frag)
  549. .children()
  550. .children('.to_remove')
  551. .removeClass('to_remove')
  552. .addClass('remove');
  553. event('loading old tab scrollback for '+name);
  554. }
  555. }
  556. tabs.push({
  557. name: name,
  558. title: title,
  559. body: frag,
  560. names: []
  561. });
  562. $tl.append($o.tabObj(tabs.length-1));
  563. $o.refreshTabs();
  564. $o.renderUsers();
  565. }else{
  566. event('Attempted to add an existing tab');
  567. }
  568. },
  569. removeTab: function(id){
  570. if(typeof tabs[id] != 'undefined'){
  571. event('Tab removed: '+tabs[id].name);
  572. socket.emit('part',{
  573. name: tabs[id].name
  574. });
  575. tabs.splice(id,1);
  576. if(selectedTab==id&&selectedTab>0){
  577. selectedTab--;
  578. }
  579. }
  580. $o.refreshTabs();
  581. $cl.html(tabs[selectedTab].body);
  582. $o.renderUsers();
  583. },
  584. tabObj: function(id){
  585. if(typeof id !== 'undefined'){
  586. return $('<div>')
  587. .addClass('tab')
  588. .text(tabs[id].title)
  589. .mouseup(function(e){
  590. switch(e.which){
  591. case 1: // RMB
  592. if($(this).data('id')!=selectedTab){
  593. $o.selectTab($(this).data('id'));
  594. return prevent(e);
  595. }
  596. break;
  597. case 2: // MMB
  598. $(this).children('span.close-button').click();
  599. return prevent(e);
  600. break;
  601. case 3: // LMB
  602. return prevent(e);
  603. break;
  604. default:
  605. return prevent(e);
  606. }
  607. })
  608. .append(
  609. $('<span>')
  610. .addClass('close-button')
  611. .click(function(){
  612. $o.removeTab(id);
  613. return false;
  614. })
  615. .css({
  616. 'position': 'absolute',
  617. 'background-color': 'inherit',
  618. 'top': 0,
  619. 'right': 0
  620. })
  621. .html('&times;')
  622. )
  623. .data('id',id);
  624. }
  625. },
  626. refreshTabs: function(){
  627. $tl.html('');
  628. var i,tab;
  629. for(i in tabs){
  630. tab = $o.tabObj(i);
  631. if(i==selectedTab){
  632. tab.addClass('clicked');
  633. $('#title').text(tabs[i].title);
  634. $('#topic').text(tabs[i].topic);
  635. }
  636. $tl.append(tab);
  637. }
  638. if($tl.get(0).scrollHeight-20 != $tl.scrollTop()){
  639. $('#tabs-scroll-right').removeClass('disabled');
  640. }
  641. if($tl.scrollTop() != 0){
  642. $('#tabs-scroll-left').removeClass('disabled');
  643. }
  644. }
  645. });
  646. String.prototype.htmlentities = function(){
  647. return this.replace(/&/g, '&amp;').replace(/\s/g, '&nbsp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
  648. };
  649. $(document).ready(function(){
  650. $.extend(settings,$.parseJSON($.localStorage('settings')));
  651. $.localStorage('settings',JSON.stringify(settings));
  652. $i = $('#input');
  653. $s = $('#send');
  654. $cl = $('#content-list');
  655. $tl = $('#tabs-list');
  656. $h = $('#head');
  657. $s.click(function(){
  658. if(!$s.hasClass('clicked')){
  659. $s.addClass('clicked');
  660. setTimeout(function(){
  661. $s.removeClass('clicked');
  662. },500);
  663. }
  664. $o.send($i.val());
  665. $i.val('');
  666. });
  667. $i.keypress(function(e){
  668. if(e.keyCode == 13){
  669. if(!$s.hasClass('clicked')){
  670. $s.addClass('clicked');
  671. setTimeout(function(){
  672. $s.removeClass('clicked');
  673. },500);
  674. }
  675. $o.send($i.val());
  676. $i.val('');
  677. }
  678. });
  679. $('#settings, #users').click(function(){
  680. $(this).addClass('open');
  681. $(this).children('.close-button').show();
  682. }).hover(function(){
  683. $(this).addClass('hovered');
  684. },function(){
  685. $(this).removeClass('hovered');
  686. }).children('.close-button').click(function(){
  687. $(this).parent().removeClass('open');
  688. $(this).hide();
  689. return false;
  690. }).hide();
  691. $('#users').hoverIntent({
  692. out: function(){
  693. $(this).removeClass('open');
  694. $(this).children('.close-button').hide();
  695. },
  696. timeout: 1000
  697. });
  698. $('#content').click(function(){
  699. $('#settings, #users, #head').removeClass('hovered').removeClass('open');
  700. $('#settings, #users').children('.close-button').hide()
  701. });
  702. $('.unselectable').attr('unselectable','on');
  703. $.contextMenu({
  704. selector: 'div.tab',
  705. items: {
  706. add: {
  707. name: 'New Tab',
  708. icon: 'add',
  709. callback: function(){
  710. $(this).contextMenu('hide');
  711. var title = prompt('Title');
  712. tabs.push({
  713. name: prompt('channel'),
  714. title: title,
  715. topic: 'Topic for '+title
  716. });
  717. $o.refreshTabs();
  718. }
  719. },
  720. s1: '',
  721. close: {
  722. name: 'Close',
  723. icon: 'delete',
  724. callback: function(){
  725. $(this).contextMenu('hide');
  726. $o.removeTab($(this).data('id'));
  727. }
  728. }
  729. },
  730. zIndex: 99999,
  731. trigger: 'right'
  732. });
  733. $.contextMenu({
  734. selector: '#tabs-list',
  735. items: {
  736. add: {
  737. name: 'New Tab',
  738. icon: 'add',
  739. callback: function(){
  740. $(this).contextMenu('hide');
  741. var title = prompt('Title');
  742. tabs.push({
  743. name: prompt('channel'),
  744. title: title,
  745. topic: 'Topic for '+title
  746. });
  747. $o.refreshTabs();
  748. }
  749. }
  750. },
  751. zIndex: 99999,
  752. trigger: 'right'
  753. });
  754. $('#tabs-scroll-right').click(function(){
  755. event('scroll right');
  756. $tl.scrollTop(($tl.scrollTop()||0)+20);
  757. if($tl.get(0).scrollHeight-20 == $tl.scrollTop()){
  758. $('#tabs-scroll-right').addClass('disabled');
  759. }
  760. $('#tabs-scroll-left').removeClass('disabled');
  761. });
  762. $('#tabs-scroll-left').click(function(){
  763. event('scroll left');
  764. $tl.scrollTop(($tl.scrollTop()||0)-20);
  765. if($tl.scrollTop() == 0){
  766. $('#tabs-scroll-left').addClass('disabled');
  767. }
  768. $('#tabs-scroll-right').removeClass('disabled');
  769. });
  770. (function scrollup(){
  771. $('#tabs-scroll-left').click();
  772. if($tl.scrollTop() != 0){
  773. setTimeout(scrollup,10);
  774. }
  775. })();
  776. //DEBUG
  777. /* for(var i=0;i<20;i++){
  778. tabs.push({
  779. name: '#Tab'+i,
  780. title: 'Tab '+i,
  781. topic: 'Topic for tab '+i
  782. });
  783. } */
  784. //END DEBUG
  785. event('Date '+new Date,'ready');
  786. $h.addClass('hovered');
  787. setTimeout(function(){
  788. $h.removeClass('hovered');
  789. },1000);
  790. $o.renderSettings();
  791. $o.connect();
  792. });
  793. delete window.io;
  794. })(window,jQuery,io);
  795. if (!Date.prototype.toISOString) {
  796. Date.prototype.toISOString = function() {
  797. function pad(n) { return n < 10 ? '0' + n : n }
  798. return this.getUTCFullYear() + '-'
  799. + pad(this.getUTCMonth() + 1) + '-'
  800. + pad(this.getUTCDate()) + 'T'
  801. + pad(this.getUTCHours()) + ':'
  802. + pad(this.getUTCMinutes()) + ':'
  803. + pad(this.getUTCSeconds()) + 'Z';
  804. };
  805. }