index.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903
  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. $(window).resize();
  284. loading(false);
  285. }else if(d.state.url != location.href){
  286. console.error('No context given');
  287. console.log(d);
  288. back();
  289. }
  290. }else{
  291. error(d);
  292. back();
  293. }
  294. flag('handled',false);
  295. });
  296. break;
  297. case 'action':break;
  298. default:
  299. error({
  300. url: State.url,
  301. error: "Unable to make a request of type "+State.data.type
  302. });
  303. }
  304. Old = State.data;
  305. }else{
  306. console.log(State.data,Old);
  307. console.warn('Stopped double load of '+Old.type+'-'+Old.id);
  308. loading(false);
  309. flag('handled',false);
  310. }
  311. },
  312. equal = function(o1,o2){
  313. for(var i in o1){
  314. if(!exists(o2[i])||o2[i]!=o1[i]){
  315. return false;
  316. }
  317. }
  318. for(i in o2){
  319. if(!exists(o1[i])||o2[i]!=o1[i]){
  320. return false;
  321. }
  322. }
  323. return true;
  324. },
  325. render = window.render = {
  326. topbar: function(t,c){
  327. $('#topbar').html(Handlebars.compile(t)(c));
  328. if(State.url == location.origin+'/page-index'){
  329. $('#topbar').find('.topbar-history').hide();
  330. }
  331. $('#topbar').addClass('overflow-hide');
  332. $('#topbar .icon-back').click(function(){
  333. back();
  334. });
  335. $('#topbar h1').text($('#topbar h1').text().capitalize());
  336. $('#topbar .icon-menu').click(function(){
  337. if($('#drawer').hasClass('open')){
  338. $('#drawer').removeClass('open').css('transform','');
  339. }else{
  340. $('#drawer').addClass('open').css('transform','translateX(80%)');
  341. }
  342. });
  343. $('#topbar .icon-add').click(function(){
  344. action('new');
  345. });
  346. render.links('#topbar');
  347. },
  348. sidebar: function(t,c){
  349. $('#sidebar').html(Handlebars.compile(t)(c));
  350. $('#toolbar-done').click(function(){
  351. $('#drawer').removeClass('open').css('transform','');
  352. });
  353. render.links('#sidebar');
  354. $('#sidebar').find('a').click(function(e){
  355. $('#drawer').removeClass('open').css('transform','');
  356. e.preventDefault();
  357. return false;
  358. });
  359. },
  360. content: function(t,c){
  361. $(document).unbind('ready');
  362. $('#content').html(
  363. Handlebars.compile(t)(c)
  364. );
  365. render.form('#content');
  366. render.accordions('#content');
  367. render.refresh('#content');
  368. },
  369. refresh: function(selector){
  370. render.links(selector);
  371. render.buttons(selector);
  372. render.menus(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. accordions: function(selector){
  388. $(selector).find('.accordion').each(function(){
  389. var icons = {};
  390. if($(this).children('.icons').length == 1){
  391. icons = JSON.parse($(this).children('.icons').text());
  392. }
  393. $(this).children('.icons').remove();
  394. $(this).accordion({
  395. collapsible: true,
  396. icons: icons,
  397. active: false
  398. });
  399. });
  400. },
  401. buttons: function(selector){
  402. $(selector).find('.more').off().each(function(){
  403. var t = $(this);
  404. if(!$.hasData(t)){
  405. t.data('type',t.text().trim());
  406. t.data('at',t.prev().children().length);
  407. t.text('Load More');
  408. }
  409. if(t.prev().children().length < 10){
  410. t.hide();
  411. }
  412. }).click(function(){
  413. var t = $(this),
  414. data = {
  415. type: 'action',
  416. id: 'more',
  417. pid: State.data.id,
  418. url: State.url,
  419. title: State.title,
  420. topbar: false,
  421. no_state: true,
  422. of: t.data('type'),
  423. at: Number(t.data('at'))
  424. };
  425. data.start = t.data('at');
  426. apiCall(data,function(d){
  427. var tmplt = Handlebars.compile(template('pages','comment')),
  428. i;
  429. if(d.messages.length < 10){
  430. t.hide();
  431. }
  432. for(i in d.messages){
  433. try{
  434. t.prev().append(
  435. tmplt(d.messages[i])
  436. );
  437. }catch(e){
  438. console.log("Could not load message: ",d.messages[i],e);
  439. }
  440. render.refresh(t.prev());
  441. }
  442. t.data('at',t.prev().children().length);
  443. },true);
  444. }).button();
  445. render.comment.buttons(selector);
  446. },
  447. comment: {
  448. buttons: function(selector){
  449. $(selector).find('.comment').each(function(){
  450. var context = JSON.parse($(this).text());
  451. $(this).text(context.text);
  452. $(this).button();
  453. $(this).click(function(){
  454. render.comment.dialog(context.id,context.type,context.title);
  455. });
  456. });
  457. },
  458. dialog: function(id,type,title){
  459. loading(true);
  460. flag('ignore_statechange',true);
  461. $('#comment').find('form').find('input[name=comment_type]').val(type);
  462. $('#comment').find('form').find('input[name=comment_id]').val(id);
  463. $('#comment').find('form').find('textarea[name=message]').val('');
  464. $('#comment').dialog({
  465. close: function(){
  466. $('#comment').find('form').find('input[name=comment_type]').val('');
  467. $('#comment').find('form').find('input[name=comment_id]').val('');
  468. flag('ignore_statechange',false);
  469. loading(false);
  470. },
  471. resizable: false,
  472. draggable: false,
  473. title: title,
  474. buttons: [
  475. {
  476. text: 'Ok',
  477. class: 'recommend-force',
  478. click: function(){
  479. var diag = $(this),
  480. context = diag.find('form').serializeObject();
  481. if(context.message !== ''){
  482. context.type = 'action';
  483. context.id = 'comment';
  484. context.url = State.url;
  485. context.title = State.title;
  486. apiCall(context,function(d){
  487. if(!exists(d.error)){
  488. diag.dialog('close');
  489. flag('ignore_statechange',false);
  490. $('.topbar-current').click();
  491. }
  492. });
  493. }
  494. }
  495. },{
  496. text: 'Cancel',
  497. class: 'cancel-force',
  498. click: function(){
  499. $(this).dialog('close');
  500. }
  501. }
  502. ]
  503. });
  504. },
  505. },
  506. menus: function(selector){
  507. $(selector).find('.menu').css({
  508. 'list-style':'none'
  509. }).menu({
  510. icons:{
  511. submenu: "ui-icon-circle-triangle-e"
  512. }
  513. }).removeClass('ui-corner-all').addClass('ui-corner-bottom').parent().click(function(e){
  514. e.stopPropagation();
  515. });
  516. },
  517. form: function(selector){
  518. render.inputs(selector);
  519. },
  520. inputs: function(selector){
  521. /*$(selector).find('input[type=text],input[type=password]').each(function(){
  522. var input = $(this),
  523. height = input.height()>=17?17:input.height();
  524. input.siblings('.input-clear').remove();
  525. input.off('focus').off('blur').after(
  526. $('<div>').css({
  527. position: 'absolute',
  528. right: $(window).width() - (input.outerWidth() + input.position().left)+2,
  529. top: input.position().top+2,
  530. 'background-image': 'url(img/headers/icons/clear.png)',
  531. 'background-position': 'center',
  532. 'background-size': height+'px '+height+'px',
  533. 'background-repeat': 'no-repeat',
  534. width: input.height(),
  535. height: input.height(),
  536. cursor: 'pointer'
  537. }).hide().addClass('input-clear').mousedown(function(){
  538. input.val('');
  539. })
  540. );
  541. input.focus(function(){
  542. input.next().show();
  543. }).blur(function(e){
  544. input.next().hide();
  545. });
  546. });
  547. $(selector).find('input[type=text],input[type=password],textarea').each(function(){
  548. var input = $(this);
  549. if(input.hasClass('fill-width')){
  550. input.css('width','calc(100% - '+(input.outerWidth()-input.width())+'px)');
  551. }
  552. });*/
  553. },
  554. dialog: function(selector,title){
  555. $(selector).dialog({
  556. close: function(){
  557. flag('error',false);
  558. var fn = $(this).data('callback');
  559. if(exists(fn)){
  560. fn();
  561. }
  562. loading(false);
  563. },
  564. resizable: false,
  565. draggable: false,
  566. title: title,
  567. buttons: [
  568. {
  569. text: 'Ok',
  570. class: 'recommend-force',
  571. click: function(){
  572. $(this).dialog('close');
  573. }
  574. }
  575. ]
  576. });
  577. },
  578. links: function(selector){
  579. $(selector).find('a').each(function(){
  580. var href = this.href;
  581. if(href.indexOf('#')!=-1&&(href.indexOf(location.origin)!=-1||href.indexOf('#')===0)){
  582. href = href.substr(href.indexOf('#')+1);
  583. $(this).click(function(e){
  584. try{
  585. loadState(href);
  586. }catch(error){
  587. console.error(error);
  588. }
  589. e.preventDefault();
  590. return false;
  591. });
  592. }
  593. });
  594. }
  595. },
  596. back = window.back = function(reload){
  597. console.log('reload',exists(reload));
  598. if(exists(State.data.back)){
  599. flag('handled',true);
  600. if(!History.back()){
  601. loadState(State.data.back);
  602. }else{
  603. stateChange();
  604. }
  605. }else if(State.data.type != 'page' || State.data.id != 'index'){
  606. if(exists(reload)){
  607. console.log('FORCING RELOAD');
  608. location.assign('page-index');
  609. }else{
  610. console.log('FORCING LOADSTATE');
  611. loadState('page-index');
  612. }
  613. }else{
  614. location.reload();
  615. }
  616. },
  617. alert = function(text,title,callback){
  618. if(exists(text)){
  619. title=exists(title)?title:'';
  620. callback=exists(callback)?callback:function(){};
  621. $('#dialog').text(text).data('callback',callback);
  622. render.dialog('#dialog',title,callback);
  623. }
  624. },
  625. hasFocus = function(){
  626. if(typeof document.hasFocus === 'undefined'){
  627. document.hasFocus = function(){
  628. return document.visibilityState == 'visible';
  629. };
  630. }
  631. return document.hasFocus();
  632. },
  633. notify = window.notify = function(title,text,onclick,onclose){
  634. var notification;
  635. if(exists(window['Notification'])&&!exists(window.webkitNotifications)&&!flag('default_notify')){
  636. if(Notification.permission === 'denied'){
  637. flag('default_notify',true);
  638. notify(title,text,onclick,onclose);
  639. }else if(Notification.permission === 'granted'){
  640. notification = new Notification(title,{
  641. body: text,
  642. icon: location.origin+'/img/favicon.ico'
  643. });
  644. notification.onclick = onclick;
  645. notification.onclose = onclose;
  646. }else{
  647. Notification.requestPermission(function(p){
  648. console.log('permission for notify: '+p);
  649. Notification.permission = p;
  650. notify(title,text,onclick,onclose);
  651. });
  652. }
  653. }else if(exists(window.navigator.mozNotification)){
  654. notification = window.navigator.mozNotification.createNotification(title,text,location.origin+'/img/favicon.ico');
  655. notification.onclick = onclick;
  656. notification.onclose = onclose;
  657. notification.show();
  658. }else{
  659. $('#notification-container').notify('create',{
  660. title: title,
  661. text: text,
  662. click: onclick,
  663. close: onclose
  664. });
  665. }
  666. },
  667. loading = function(state){
  668. if(!flag('ignore_statechange')){
  669. state = exists(state)?state:false;
  670. console.log('loading state '+state);
  671. if(!flag('error') && !state){
  672. $('#loading').hide();
  673. }else if(state){
  674. $('#loading').show();
  675. }
  676. }
  677. },
  678. debug = window.debug = {
  679. hardReload: function(){
  680. debug.clearCache();
  681. location.reload();
  682. },
  683. clearCache: function(){
  684. templates = [];
  685. $.localStorage('templates',null);
  686. console.log('Templates cleared.');
  687. },
  688. manifesto: function(){
  689. if(!flag('manifesto')){
  690. if(window.applicationCache){
  691. if(window.applicationCache.status==window.applicationCache.UNCACHED){
  692. $('head').append(
  693. $('<script>').attr({
  694. 'type': 'text/javascript',
  695. 'src': 'http://manifesto.ericdelabar.com/manifesto.js?x="+(Math.random())'
  696. })
  697. );
  698. (function wait(){
  699. if($('#cacheStatus').length === 0){
  700. setTimeout(wait,10);
  701. }else{
  702. $('#cacheStatus').niceScroll();
  703. }
  704. })();
  705. }else{
  706. alert("Manifest file is valid.");
  707. }
  708. }else{
  709. alert("This browser does not support HTML5 Offline Application Cache.");
  710. }
  711. flag('manifesto',true);
  712. }
  713. },
  714. firebug: function(){
  715. if(!flag('firebug-lite')){
  716. $('head').append(
  717. $('<script>').attr({
  718. 'type': 'text/javascript',
  719. 'src': 'https://getfirebug.com/firebug-lite.js#startOpened',
  720. 'id': 'FirebugLite'
  721. })
  722. );
  723. $('<image>').attr('src','https://getfirebug.com/releases/lite/latest/skin/xp/sprite.png');
  724. flag('firebug-lite',true);
  725. }
  726. }
  727. },
  728. getTemplates = function(callback){
  729. var fn = function(type,callback){
  730. $.get('api.php',{
  731. type: 'manifest',
  732. id: type
  733. },function(d){
  734. if(!exists(d.error)){
  735. var count = d.manifest.length;
  736. for(var i in d.manifest){
  737. console.log('Loading template('+(Number(i)+1)+'/'+d.manifest.length+'): '+d.manifest[i].name);
  738. if(templateHash(type,d.manifest[i].name) !== d.manifest[i].hash){
  739. $.get('api.php',{
  740. type: 'template',
  741. id: type,
  742. name: d.manifest[i].name
  743. },function(d){
  744. template(d.type,d.name,d.template,d.hash);
  745. $.localStorage('templates',templates);
  746. count--;
  747. console.log('Loaded template('+count+' left): '+d.name);
  748. },'json');
  749. }else{
  750. count--;
  751. }
  752. }
  753. setTimeout(function wait_for_templates(){
  754. if(count === 0){
  755. console.log('getTemplates callback');
  756. callback();
  757. }else{
  758. setTimeout(wait_for_templates,10);
  759. }
  760. },10);
  761. }else{
  762. error(d.error);
  763. }
  764. },'json');
  765. };
  766. fn('topbars',function(){
  767. fn('sidebars',function(){
  768. fn('pages',function(){
  769. callback();
  770. });
  771. });
  772. });
  773. };
  774. if(exists($.cookie('key'))){
  775. setKey($.cookie('key'));
  776. }else{
  777. setKey(null);
  778. }
  779. $(document).ready(function(){
  780. if(exists(window['Notification'])&&exists(Notification.permission)&&Notification.permission !== 'granted'){
  781. Notification.requestPermission();
  782. }
  783. $.ajaxSetup({
  784. async: false,
  785. cache: false,
  786. timeout: 30000 // 30 seconds
  787. });
  788. $(document).ajaxError(function(event, request, settings) {
  789. error({error:'Request timed out'});
  790. });
  791. if(!exists($.support.touch)){
  792. $.support.touch = 'ontouchstart' in window || 'onmsgesturechange' in window;
  793. }
  794. $('#content,#topbar').click(function(){
  795. $('.menu').hide();
  796. });
  797. $(window).resize(function(){
  798. if($(window).width()>767){
  799. $('#topbar div.topbar-right, #topbar div.topbar-left').css({
  800. 'display': ''
  801. });
  802. }
  803. render.inputs('#content');
  804. render.inputs('#topbar');
  805. });
  806. $.get(location.href,{
  807. get: 'settings',
  808. timestamp: +new Date,
  809. back: false,
  810. no_state: true
  811. },function(d){
  812. if(!exists(d.error)){
  813. settings = d.settings;
  814. if(d.version != $.localStorage('version')){
  815. $.localStorage('version',d.version);
  816. $.localStorage('templates',null);
  817. templates = [];
  818. }else{
  819. templates = $.localStorage('templates');
  820. if(templates === null){
  821. templates = [];
  822. }
  823. }
  824. getTemplates(function(){
  825. replaceState(location.href);
  826. (function notifications(){
  827. var context = State;
  828. context.type = 'action';
  829. context.id = 'notifications';
  830. context.url = State.url;
  831. context.title = State.title;
  832. context.topbar = false;
  833. context.no_state = true;
  834. apiCall(context,function(d){
  835. if(!exists(d.error)){
  836. if(d.count>0 && $.localStorage('last_pm_check') < d.timestamp){
  837. notify('Alert','You have '+d.count+' new message'+(d.count>1?'s':''),function(){
  838. loadState('page-messages');
  839. });
  840. }
  841. $('.topbar-notifications').css('display',d.count>0?'block':'').text('('+d.count+')');
  842. $.localStorage('last_pm_check',d.timestamp);
  843. }
  844. setTimeout(notifications,5*1000); // every 5 seconds
  845. },true);
  846. })();
  847. });
  848. }else{
  849. error(d.error);
  850. }
  851. },'json');
  852. $(window).on('statechange',function(){
  853. if(!flag('handled')){
  854. console.log('unhandled state change event');
  855. console.trace();
  856. stateChange();
  857. }else{
  858. flag('handled',false);
  859. }
  860. });
  861. var getState = History.getState;
  862. History.getState = function(flag){
  863. if(exists(flag)){
  864. return State;
  865. }else{
  866. return getState.call(History);
  867. }
  868. };
  869. $('#notification-container').notify();
  870. });
  871. shortcut.add('f12',function(){
  872. debug.firebug();
  873. });
  874. shortcut.add('Ctrl+f12',function(){
  875. debug.manifesto();
  876. });
  877. shortcut.add('Shift+f12',function(){
  878. debug.clearCache();
  879. });
  880. $.fn.serializeObject = function(){
  881. var o = {},
  882. a = this.serializeArray();
  883. $.each(a,function(){
  884. if(o[this.name] !== undefined){
  885. if(!o[this.name].push){
  886. o[this.name] = [o[this.name]];
  887. }
  888. o[this.name].push(this.value || '');
  889. }else{
  890. o[this.name] = this.value || '';
  891. }
  892. });
  893. return o;
  894. };
  895. String.prototype.capitalize = function(lower) {
  896. return (lower?this.toLowerCase():this).replace(/(?:^|\s)\S/g, function(a){
  897. return a.toUpperCase();
  898. });
  899. };
  900. })(jQuery,History,console);