index.js 22 KB

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