index.js 22 KB

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