index.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816
  1. // TODO - Add initial page loading and handlers
  2. (function($,History,console){
  3. var State = History.getState(),
  4. Old = {},
  5. Key = null,
  6. flags = [],
  7. templates = [],
  8. flag = window.flag = function(name,value){
  9. if(exists(value)){
  10. flags[name] = value;
  11. }else{
  12. return exists(flags[name])?flags[name]:false;
  13. }
  14. },
  15. settings = {},
  16. exists = function(v){
  17. return typeof v != 'undefined';
  18. },
  19. get = window.get = function(s){
  20. return settings[s];
  21. },
  22. set = window.set = function(s,v){
  23. settings[s] = v;
  24. return v;
  25. },
  26. setKey = window.setKey = function(key){
  27. if(key !== null){
  28. console.log('Key change to '+key);
  29. Key = key;
  30. var d = new Date();
  31. d.setTime(d.getTime()+get('expire'));
  32. $.cookie('key',key,{
  33. expires: d
  34. });
  35. }else{
  36. console.log('Key deleted');
  37. Key = null;
  38. $.removeCookie('key');
  39. }
  40. },
  41. getKey = window.getKey = function(){
  42. return Key;
  43. },
  44. template = window.template = function(type,name,template){
  45. var d = +new Date,
  46. id = (function(type,name){
  47. for(var i in templates){
  48. if(templates[i].name == name && templates[i].type == type){
  49. return i;
  50. }
  51. }
  52. return false;
  53. })(type,name);
  54. if(exists(template)){
  55. if(template === null){
  56. if(id!==false){
  57. templates.splice(id,1);
  58. }
  59. $.localStorage('templates',templates);
  60. console.log('Dropping template for: '+name);
  61. return '';
  62. }else{
  63. var o = {
  64. name: name,
  65. template: template,
  66. type: type,
  67. date: Number(get('expire'))+Number(d)
  68. }
  69. if(id===false){
  70. console.log('Storing new template for: '+name);
  71. templates.push(o);
  72. }else{
  73. console.log('Replacing old template for: '+name);
  74. templates[id] = o;
  75. }
  76. $.localStorage('templates',templates);
  77. }
  78. }else if(id!==false){
  79. console.log('Using cached template for: '+name);
  80. var template = templates[id].template;
  81. if(templates[id].date < d){
  82. delete templates[name];
  83. $.localStorage('templates',templates);
  84. }
  85. return template;
  86. }else{
  87. console.log('No cached template stored for: '+type+':'+name);
  88. return '';
  89. }
  90. },
  91. apiCall = window.apiCall = function(data,callback,background){
  92. console.log('apiCall('+data.type+'-'+data.id+')');
  93. if(!flag('error')){
  94. if(exists(background)&&!background){
  95. loading(true);
  96. }
  97. data.get = 'api';
  98. data.back = State.data.back;
  99. data.timestamp = +new Date;
  100. $.get(location.href,data,function(d){
  101. if(exists(d['error'])){
  102. error(d);
  103. }else{
  104. if(exists(d.state)){
  105. d.state.title = (d.state.title+'').capitalize();
  106. if(location.href.substr(location.href.lastIndexOf('/')+1) != d.state.url && d.state.url !== ''){
  107. console.log('Forced redirection to '+d.state.url);
  108. History.replaceState(d.state.data,d.state.title,d.state.url);
  109. getNewState();
  110. }
  111. document.title = d.state.title;
  112. }
  113. }
  114. if(exists(callback)){
  115. console.log('Running apiCall callback');
  116. try{
  117. callback(d);
  118. }catch(e){
  119. error(e);
  120. }
  121. }
  122. },'json');
  123. }
  124. },
  125. loadState = window.loadState = function(href,callback){
  126. console.log('loadState('+href+')');
  127. if(!flag('error')){
  128. loading(true);
  129. var data = {
  130. get:'state',
  131. timestamp: +new Date,
  132. back: location.href
  133. };
  134. ajax = $.ajax(href,{
  135. data: data,
  136. async: true,
  137. type: 'GET',
  138. success: function(d){
  139. if(exists(d['error'])){
  140. error(d);
  141. }else{
  142. d.state.title = d.state.title.capitalize();
  143. d.state.url = href;
  144. console.log('pushState: '+d.state.title+'['+href+']');
  145. History.pushState(d.state.data,d.state.title,href);
  146. getNewState();
  147. }
  148. if(exists(callback)){
  149. callback(d);
  150. }
  151. if(!exists(d['error'])){
  152. flag('handled',true);
  153. stateChange();
  154. flag('handled',false);
  155. }
  156. },
  157. error: function(x,t,e){
  158. error({
  159. error: '['+t+'] '+e
  160. });
  161. if(exists(callback)){
  162. callback({
  163. error: '['+t+'] '+e
  164. });
  165. }
  166. },
  167. dataType: 'json'
  168. });
  169. }
  170. },
  171. replaceState = window.replaceState = function(href,callback){
  172. console.log('apiState('+href+')');
  173. if(!flag('error')){
  174. loading(true);
  175. var data = {
  176. get:'state',
  177. timestamp: +new Date,
  178. back: State.data.back
  179. };
  180. $.ajax(href,{
  181. data: data,
  182. async: true,
  183. type: 'GET',
  184. success: function(d){
  185. if(exists(d['error'])){
  186. error(d);
  187. }else{
  188. d.state.title = d.state.title.capitalize();
  189. d.state.url = href;
  190. console.log('pushState: '+d.state.title+'['+href+']');
  191. History.replaceState(d.state.data,d.state.title,href);
  192. getNewState();
  193. }
  194. if(exists(callback)){
  195. callback(d);
  196. }
  197. console.log(d.state.title);
  198. if(!exists(d['error'])){
  199. flag('handled',true);
  200. stateChange();
  201. flag('handled',false);
  202. }
  203. },
  204. error: function(x,t,e){
  205. error({
  206. error: '['+t+'] '+e
  207. });
  208. if(exists(callback)){
  209. callback({
  210. error: '['+t+'] '+e
  211. });
  212. }
  213. },
  214. dataType: 'json'
  215. });
  216. }
  217. },
  218. error = window.error = function(e,callback){
  219. if(!flag('error')){
  220. flag('error',true);
  221. var msg = '['+State.url+']'+e.error;
  222. console.error((msg.trim()+"\n"+(exists(e.state)?JSON.stringify(e.state):'')).trim());
  223. alert(msg.trim(),'Error',callback);
  224. console.trace();
  225. }
  226. },
  227. getNewState = function(state){
  228. State = History.getState();
  229. console.log("State change: ["+State.title+"] "+JSON.stringify(State.data));
  230. if(!window.location.origin){
  231. window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '');
  232. }
  233. },
  234. stateChange = function(){
  235. getNewState();
  236. if(!equal(State.data,Old)){
  237. switch(State.data.type){
  238. case 'page':case 'user':case 'project':case 'issue':
  239. apiCall(State.data,function(d){
  240. if(!exists(d.error)){
  241. if(exists(d.context)){
  242. if(!exists(d.context.key)&&Key!==null){
  243. console.log('Context detected console.logout');
  244. setKey(null);
  245. }
  246. render.topbar(template('topbars',d.topbar.template),d.topbar.context);
  247. if(exists(d.template)){
  248. console.log('Using template: '+d.template.type+':'+d.template.name);
  249. d.template = template(d.template.type,d.template.name);
  250. render.content(d.template,d.context);
  251. }else{
  252. console.log('No template used');
  253. }
  254. $(window).resize();
  255. loading(false);
  256. }else{
  257. console.error('No context given');
  258. back();
  259. }
  260. }else{
  261. error(d);
  262. back();
  263. }
  264. });
  265. break;
  266. case 'action':break;
  267. default:
  268. error({
  269. url: State.url,
  270. error: "Unable to make a request of type "+State.data.type
  271. });
  272. }
  273. Old = State.data;
  274. }else{
  275. console.log(State.data,Old);
  276. console.warn('Stopped double load of '+Old.type+'-'+Old.id);
  277. loading(false);
  278. }
  279. },
  280. equal = function(o1,o2){
  281. for(var i in o1){
  282. if(!exists(o2[i])||o2[i]!=o1[i]){
  283. return false;
  284. }
  285. }
  286. for(i in o2){
  287. if(!exists(o1[i])||o2[i]!=o1[i]){
  288. return false;
  289. }
  290. }
  291. return true;
  292. },
  293. render = window.render = {
  294. topbar: function(t,c){
  295. $('#topbar').html(Handlebars.compile(t)(c));
  296. render.links('#topbar');
  297. render.buttons('#topbar');
  298. render.menus('#topbar');
  299. render.time('#topbar');
  300. if(State.url == location.origin+'/page-index'){
  301. $('#topbar').find('.topbar-history').hide();
  302. }
  303. $('#topbar').addClass('overflow-hide');
  304. $(window).resize();
  305. },
  306. content: function(t,c){
  307. $(document).unbind('ready');
  308. $('#content').html(
  309. Handlebars.compile(t)(c)
  310. );
  311. render.links('#content');
  312. render.buttons('#content');
  313. render.accordions('#content');
  314. render.menus('#content');
  315. render.form('#content');
  316. render.time('#content');
  317. $(window).resize();
  318. },
  319. time: function(selector){
  320. $(selector).find('time.timeago').each(function(){
  321. var time = new Date($(this).text()*1000);
  322. $(this).replaceWith(
  323. $('<abbr>').attr({
  324. 'title': time.toISOString(),
  325. 'style': $(this).attr('style')
  326. }).addClass($(this).attr('class')).timeago()
  327. );
  328. });
  329. },
  330. accordions: function(selector){
  331. $(selector).find('.accordion').each(function(){
  332. var icons = {};
  333. if($(this).children('.icons').length == 1){
  334. icons = JSON.parse($(this).children('.icons').text());
  335. }
  336. $(this).children('.icons').remove();
  337. $(this).accordion({
  338. collapsible: true,
  339. icons: icons,
  340. active: false
  341. });
  342. });
  343. },
  344. buttons: function(selector){
  345. $(selector).find('.button').button();
  346. $(selector).find('input[type=submit]').button();
  347. $(selector).find('input[type=button]').button();
  348. $(selector).find('button').button();
  349. render.comment.buttons(selector);
  350. },
  351. comment: {
  352. buttons: function(selector){
  353. $(selector).find('.comment').each(function(){
  354. var context = JSON.parse($(this).text());
  355. $(this).text(context.text);
  356. $(this).button();
  357. $(this).click(function(){
  358. render.comment.dialog(context.id,context.type,context.title);
  359. });
  360. });
  361. },
  362. dialog: function(id,type,title){
  363. loading(true);
  364. flag('ignore_statechange',true);
  365. $('#comment').find('form').find('input[name=comment_type]').val(type);
  366. $('#comment').find('form').find('input[name=comment_id]').val(id);
  367. $('#comment').find('form').find('textarea[name=message]').val('');
  368. $('#comment').dialog({
  369. close: function(){
  370. $('#comment').find('form').find('input[name=comment_type]').val('');
  371. $('#comment').find('form').find('input[name=comment_id]').val('');
  372. flag('ignore_statechange',false);
  373. loading(false);
  374. },
  375. resizable: false,
  376. draggable: false,
  377. title: title,
  378. buttons: [
  379. {
  380. text: 'Ok',
  381. class: 'recommend-force',
  382. click: function(){
  383. var diag = $(this),
  384. context = diag.find('form').serializeObject();
  385. if(context.message !== ''){
  386. context.type = 'action';
  387. context.id = 'comment';
  388. context.url = State.url;
  389. context.title = State.title;
  390. apiCall(context,function(d){
  391. if(!exists(d.error)){
  392. diag.dialog('close');
  393. flag('ignore_statechange',false);
  394. $('.topbar-current').click();
  395. }
  396. });
  397. }
  398. }
  399. },{
  400. text: 'Cancel',
  401. class: 'cancel-force',
  402. click: function(){
  403. $(this).dialog('close');
  404. }
  405. }
  406. ]
  407. });
  408. },
  409. },
  410. menus: function(selector){
  411. $(selector).find('.menu').css({
  412. 'list-style':'none'
  413. }).menu({
  414. icons:{
  415. submenu: "ui-icon-circle-triangle-e"
  416. }
  417. }).removeClass('ui-corner-all').addClass('ui-corner-bottom').parent().click(function(e){
  418. e.stopPropagation();
  419. });
  420. },
  421. form: function(selector){
  422. $(selector).find('#form').width('320px').children();
  423. render.inputs(selector);
  424. },
  425. inputs: function(selector){
  426. $(selector).find('input[type=text],input[type=password]').each(function(){
  427. var input = $(this),
  428. height = input.height()>=17?17:input.height();
  429. input.siblings('.input-clear').remove();
  430. input.off('focus').off('blur').after(
  431. $('<div>').css({
  432. position: 'absolute',
  433. right: $(window).width() - (input.outerWidth() + input.position().left)+2,
  434. top: input.position().top+2,
  435. 'background-image': 'url(img/headers/icons/clear.png)',
  436. 'background-position': 'center',
  437. 'background-size': height+'px '+height+'px',
  438. 'background-repeat': 'no-repeat',
  439. width: input.height(),
  440. height: input.height(),
  441. cursor: 'pointer'
  442. }).hide().addClass('input-clear').mousedown(function(){
  443. input.val('');
  444. })
  445. );
  446. input.focus(function(){
  447. input.next().show();
  448. }).blur(function(e){
  449. input.next().hide();
  450. });
  451. });
  452. $(selector).find('input[type=text],input[type=password],textarea').each(function(){
  453. var input = $(this);
  454. if(input.hasClass('fill-width')){
  455. input.css('width','calc(100% - '+(input.outerWidth()-input.width())+'px)');
  456. }
  457. });
  458. },
  459. dialog: function(selector,title){
  460. $(selector).dialog({
  461. close: function(){
  462. flag('error',false);
  463. var fn = $(this).data('callback');
  464. if(exists(fn)){
  465. fn();
  466. }
  467. loading(false);
  468. },
  469. resizable: false,
  470. draggable: false,
  471. title: title,
  472. buttons: [
  473. {
  474. text: 'Ok',
  475. class: 'recommend-force',
  476. click: function(){
  477. $(this).dialog('close');
  478. }
  479. }
  480. ]
  481. });
  482. },
  483. links: function(selector){
  484. $(selector).find('a').each(function(){
  485. var href = this.href;
  486. if(href.indexOf('#')!=-1&&(href.indexOf(location.origin)!=-1||href.indexOf('#')==0)){
  487. href = href.substr(href.indexOf('#')+1);
  488. $(this).click(function(e){
  489. try{
  490. if(($(this).hasClass('topbar-home') || $(this).hasClass('topbar-back'))&&$(window).width()<767){
  491. $('#topbar').children('div.topbar-right,div.topbar-left').toggle();
  492. $('#topbar').toggleClass('overflow-hide');
  493. $(window).resize();
  494. }else if($(this).hasClass('topbar-history')){
  495. back();
  496. }else if($(this).hasClass('topbar-current')){
  497. replaceState(href);
  498. }else{
  499. loadState(href);
  500. }
  501. }catch(error){
  502. console.error(error);
  503. }
  504. e.preventDefault();
  505. return false;
  506. });
  507. }
  508. });
  509. }
  510. },
  511. back = window.back = function(reload){
  512. console.log('reload',exists(reload));
  513. var go = function(url){
  514. if(exists(reload)){
  515. console.log('FORCING RELOAD');
  516. location = url;
  517. }else{
  518. console.log('FORCING LOADSTATE');
  519. loadState(url);
  520. }
  521. }
  522. if(exists(State.data.back)){
  523. if(!History.back()){
  524. loadState(State.data.back);
  525. }
  526. }else if(State.data.type != 'page' || State.data.id != 'index'){
  527. go('page-index');
  528. }else{
  529. location.reload();
  530. }
  531. },
  532. alert = function(text,title,callback){
  533. if(exists(text)){
  534. title=exists(title)?title:'';
  535. callback=exists(callback)?callback:function(){};
  536. $('#dialog').text(text).data('callback',callback);
  537. render.dialog('#dialog',title,callback);
  538. }
  539. },
  540. hasFocus = function(){
  541. if(typeof document.hasFocus === 'undefined'){
  542. document.hasFocus = function(){
  543. return document.visibilityState == 'visible';
  544. }
  545. }
  546. return document.hasFocus();
  547. },
  548. notify = window.notify = function(title,text,onclick,onclose){
  549. var notification;
  550. if(exists(window.Notification)&&!exists(window.webkitNotifications)&&!flag('default_notify')&&!hasFocus()){
  551. if(Notification.permission === 'denied'){
  552. flag('default_notify',true);
  553. notify(title,text,onclick,onclose);
  554. }else if(Notification.permission === 'granted'){
  555. notification = new Notification(title,{
  556. body: text,
  557. icon: location.origin+'/img/favicon.ico'
  558. });
  559. notification.onclick = onclick;
  560. notification.onclose = onclose;
  561. }else{
  562. Notification.requestPermission(function(p){
  563. console.log('permission for notify: '+p);
  564. Notification.permission = p;
  565. notify(title,text,onclick,onclose);
  566. });
  567. }
  568. }else if(exists(window.navigator.mozNotification)&&!hasFocus()){
  569. notification = window.navigator.mozNotification.createNotification(title,text,location.origin+'/img/favicon.ico');
  570. notification.onclick = onclick;
  571. notification.onclose = onclose;
  572. notification.show();
  573. }else{
  574. $('#notification-container').notify('create',{
  575. title: title,
  576. text: text,
  577. click: onclick,
  578. close: onclose
  579. });
  580. }
  581. },
  582. loading = function(state){
  583. if(!flag('ignore_statechange')){
  584. state = exists(state)?state:false;
  585. console.log('loading state '+state);
  586. if(!flag('error') && !state){
  587. $('#loading').hide();
  588. }else if(state){
  589. $('#loading').show();
  590. }
  591. }
  592. },
  593. debug = window.debug = {
  594. hardReload: function(){
  595. debug.clearCache();
  596. location.reload();
  597. },
  598. clearCache: function(){
  599. templates = [];
  600. $.localStorage('templates',null);
  601. console.log('Templates cleared.');
  602. },
  603. manifesto: function(){
  604. if(!flag('manifesto')){
  605. if(window.applicationCache){
  606. if(window.applicationCache.status==window.applicationCache.UNCACHED){
  607. $('head').append(
  608. $('<script>').attr({
  609. 'type': 'text/javascript',
  610. 'src': 'http://manifesto.ericdelabar.com/manifesto.js?x="+(Math.random())'
  611. })
  612. );
  613. (function wait(){
  614. if($('#cacheStatus').length === 0){
  615. setTimeout(wait,10);
  616. }else{
  617. $('#cacheStatus').niceScroll();
  618. }
  619. })();
  620. }else{
  621. alert("Manifest file is valid.");
  622. }
  623. }else{
  624. alert("This browser does not support HTML5 Offline Application Cache.");
  625. }
  626. flag('manifesto',true);
  627. }
  628. },
  629. firebug: function(){
  630. if(!flag('firebug-lite')){
  631. $('head').append(
  632. $('<script>').attr({
  633. 'type': 'text/javascript',
  634. 'src': 'https://getfirebug.com/firebug-lite.js#startOpened',
  635. 'id': 'FirebugLite'
  636. })
  637. );
  638. $('<image>').attr('src','https://getfirebug.com/releases/lite/latest/skin/xp/sprite.png');
  639. flag('firebug-lite',true);
  640. }
  641. }
  642. },
  643. getTemplates = function(callback){
  644. var fn = function(type,callback){
  645. $.get('api.php',{
  646. type: 'manifest',
  647. id: type
  648. },function(d){
  649. if(!exists(d.error)){
  650. var count = d.manifest.length,
  651. m = +new Date;
  652. for(var i in d.manifest){
  653. console.log('Loading template('+(Number(i)+1)+'/'+d.manifest.length+'): '+d.manifest[i]);
  654. if(template(type,d.manifest[i]) === ''){
  655. $.get('api.php',{
  656. type: 'template',
  657. id: type,
  658. name: d.manifest[i]
  659. },function(d){
  660. templates.push({
  661. name: d.name,
  662. template: d.template,
  663. type: d.type,
  664. date: Number(get('expire'))+Number(m)
  665. });
  666. $.localStorage('templates',templates);
  667. count--;
  668. console.log('Loaded template('+count+' left): '+d.name);
  669. },'json');
  670. }else{
  671. count--;
  672. }
  673. }
  674. setTimeout(function wait_for_templates(){
  675. if(count === 0){
  676. console.log('getTemplates callback');
  677. callback();
  678. }else{
  679. setTimeout(wait_for_templates,10);
  680. }
  681. },10);
  682. }else{
  683. error(d.error);
  684. }
  685. },'json');
  686. };
  687. fn('topbars',function(){
  688. fn('pages',function(){
  689. callback();
  690. });
  691. });
  692. };
  693. if(exists($.cookie('key'))){
  694. setKey($.cookie('key'));
  695. }else{
  696. setKey(null);
  697. }
  698. $(document).ready(function(){
  699. if(exists(typeof Notification.permission)&&Notification.permission !== 'granted'){
  700. Notification.requestPermission();
  701. }
  702. $.ajaxSetup({
  703. async: false,
  704. cache: false,
  705. timeout: 30000 // 30 seconds
  706. });
  707. $(document).ajaxError(function(event, request, settings) {
  708. error({error:'Request timed out'});
  709. });
  710. if(!exists($.support.touch)){
  711. $.support.touch = 'ontouchstart' in window || 'onmsgesturechange' in window;
  712. }
  713. $('#content,#topbar').click(function(){
  714. $('.menu').hide();
  715. });
  716. $(window).resize(function(){
  717. if($(window).width()>767){
  718. $('#topbar div.topbar-right, #topbar div.topbar-left').css({
  719. 'display': ''
  720. });
  721. }
  722. render.inputs('#content');
  723. render.inputs('#topbar');
  724. });
  725. $.get(location.href,{
  726. get: 'settings',
  727. timestamp: +new Date,
  728. back: false,
  729. no_state: true
  730. },function(d){
  731. if(!exists(d.error)){
  732. settings = d.settings;
  733. if(d.version != $.localStorage('version')){
  734. $.localStorage('version',d.version);
  735. $.localStorage('templates',null);
  736. templates = [];
  737. }else{
  738. templates = $.localStorage('templates');
  739. if(templates === null){
  740. templates = [];
  741. }
  742. }
  743. getTemplates(function(){
  744. replaceState(location.href);
  745. (function notifications(){
  746. var context = State;
  747. context.type = 'action';
  748. context.id = 'notifications';
  749. context.url = State.url;
  750. context.title = State.title;
  751. context.topbar = false;
  752. context.no_state = true;
  753. apiCall(context,function(d){
  754. if(!exists(d.error)){
  755. if(d.count>0 && $.localStorage('last_pm_check') < d.timestamp){
  756. notify('Alert','You have '+d.count+' new message'+(d.count>1?'s':''),function(){
  757. loadState('page-messages');
  758. });
  759. }
  760. $('.topbar-notifications').css('display',d.count>0?'block':'').text('('+d.count+')');
  761. $.localStorage('last_pm_check',d.timestamp);
  762. }
  763. setTimeout(notifications,5*1000); // every 5 seconds
  764. },true);
  765. })();
  766. });
  767. }else{
  768. error(d.error);
  769. }
  770. },'json');
  771. $(window).on('statechange',function(){
  772. if(!flag('handled')){
  773. console.log('unhandled state change event');
  774. stateChange();
  775. }
  776. });
  777. var getState = History.getState;
  778. History.getState = function(flag){
  779. if(exists(flag)){
  780. return State;
  781. }else{
  782. return getState.call(History);
  783. }
  784. };
  785. $('#notification-container').notify();
  786. });
  787. shortcut.add('f12',function(){
  788. debug.firebug();
  789. });
  790. shortcut.add('Ctrl+f12',function(){
  791. debug.manifesto();
  792. });
  793. shortcut.add('Shift+f12',function(){
  794. debug.clearCache();
  795. });
  796. $.fn.serializeObject = function(){
  797. var o = {},
  798. a = this.serializeArray();
  799. $.each(a,function(){
  800. if(o[this.name] !== undefined){
  801. if(!o[this.name].push){
  802. o[this.name] = [o[this.name]];
  803. }
  804. o[this.name].push(this.value || '');
  805. }else{
  806. o[this.name] = this.value || '';
  807. }
  808. });
  809. return o;
  810. };
  811. String.prototype.capitalize = function(lower) {
  812. return (lower?this.toLowerCase():this).replace(/(?:^|\s)\S/g, function(a){
  813. return a.toUpperCase();
  814. });
  815. };
  816. })(jQuery,History,console);