index.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  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(name,template){
  45. var d = +new Date,
  46. id = (function(name){
  47. for(var i in templates){
  48. if(templates[i].name == name){
  49. return i;
  50. }
  51. }
  52. return false;
  53. })(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. date: get('expire')+d
  67. }
  68. if(id===false){
  69. console.log('Storing new template for: '+name);
  70. templates.push(o);
  71. }else{
  72. console.log('Replacing old template for: '+name);
  73. templates[id] = o;
  74. }
  75. $.localStorage('templates',templates);
  76. }
  77. }else if(id!==false){
  78. console.log('Using cached template for: '+name);
  79. var template = templates[id].template;
  80. if(templates[id].date < d){
  81. delete templates[name];
  82. $.localStorage('templates',templates);
  83. }
  84. return template;
  85. }else{
  86. console.log('No cached template stored for: '+name);
  87. return '';
  88. }
  89. },
  90. apiCall = window.apiCall = function(data,callback){
  91. console.log('apiCall('+data.type+'-'+data.id+')');
  92. loading(true);
  93. data.get = 'api';
  94. data.back = State.data.back;
  95. data.timestamp = +new Date;
  96. if(''!=template(data.type+'-'+data.id)){
  97. data.template = false;
  98. }
  99. $.get(location.href,data,function(d){
  100. if(exists(d['error'])){
  101. error(d);
  102. }else{
  103. if(location.href.substr(location.href.lastIndexOf('/')+1) != d.state.url){
  104. console.log('Forced redirection to '+d.state.url);
  105. History.replaceState(d.state.data,d.state.title,d.state.url);
  106. getNewState();
  107. }
  108. }
  109. if(exists(callback)){
  110. console.log('Running apiCall callback');
  111. callback(d);
  112. }
  113. },'json');
  114. },
  115. loadState = window.loadState = function(href,callback){
  116. console.log('loadState('+href+')');
  117. loading(true);
  118. var data = {
  119. get:'state',
  120. timestamp: +new Date,
  121. back: location.href
  122. };
  123. ajax = $.ajax(href,{
  124. data: data,
  125. async: true,
  126. type: 'GET',
  127. success: function(d){
  128. if(exists(d['error'])){
  129. error(d);
  130. }else{
  131. console.log('pushState: '+d.state.title+'['+href+']');
  132. History.pushState(d.state.data,d.state.title,href);
  133. getNewState();
  134. }
  135. if(exists(callback)){
  136. callback(d);
  137. }
  138. if(!exists(d['error'])){
  139. flag('handled',true);
  140. stateChange();
  141. flag('handled',false);
  142. }
  143. },
  144. error: function(x,t,e){
  145. error({
  146. error: '['+t+'] '+e
  147. });
  148. if(exists(callback)){
  149. callback({
  150. error: '['+t+'] '+e
  151. });
  152. }
  153. },
  154. dataType: 'json'
  155. });
  156. },
  157. apiState = window.apiState = function(href,callback){
  158. console.log('apiState('+href+')');
  159. loading(true);
  160. var data = {
  161. get:'state',
  162. timestamp: +new Date,
  163. back: State.data.back
  164. };
  165. $.ajax(href,{
  166. data: data,
  167. async: true,
  168. type: 'GET',
  169. success: function(d){
  170. if(exists(d['error'])){
  171. error(d);
  172. }else{
  173. console.log('pushState: '+d.state.title+'['+href+']');
  174. History.replaceState(d.state.data,d.state.title,href);
  175. getNewState();
  176. }
  177. if(exists(callback)){
  178. callback(d);
  179. }
  180. console.log(d.state.title);
  181. if(!exists(d['error'])){
  182. flag('handled',true);
  183. stateChange();
  184. flag('handled',false);
  185. }
  186. },
  187. error: function(x,t,e){
  188. error({
  189. error: '['+t+'] '+e
  190. });
  191. if(exists(callback)){
  192. callback({
  193. error: '['+t+'] '+e
  194. });
  195. }
  196. },
  197. dataType: 'json'
  198. });
  199. },
  200. error = function(e){
  201. if(!flag('error')){
  202. flag('error',true);
  203. var msg = '['+State.url+']'+e.error;
  204. console.error(msg.trim()+"\n"+(exists(e.state)?JSON.stringify(e.state):''));
  205. alert(msg.trim());
  206. }
  207. },
  208. getNewState = function(){
  209. State = History.getState();
  210. console.log("State change: ["+State.title+"] "+JSON.stringify(State.data));
  211. if(!window.location.origin){
  212. window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '');
  213. }
  214. document.title = State.title;
  215. },
  216. stateChange = function(){
  217. getNewState();
  218. if(!equal(State.data,Old)){
  219. switch(State.data.type){
  220. case 'page':case 'user':case 'project':
  221. apiCall(State.data,function(d){
  222. if(!exists(d.error)){
  223. if(exists(d.context)){
  224. if(!exists(d.context.key)&&Key!==null){
  225. console.log('Context detected console.logout');
  226. setKey(null);
  227. }
  228. if(exists(d.template)){
  229. template(State.data.type+'-'+State.data.id,d.template);
  230. }else{
  231. d.template = template(State.data.type+'-'+State.data.id);
  232. }
  233. render.topbar(d.topbar.template,d.topbar.context);
  234. render.content(d.template,d.context);
  235. $(window).resize();
  236. loading(false);
  237. }else{
  238. console.error('No context given');
  239. back();
  240. }
  241. }else{
  242. error(d);
  243. back();
  244. }
  245. });
  246. break;
  247. case 'action':break;
  248. default:
  249. error({
  250. url: State.url,
  251. error: "Something went wrong!"
  252. });
  253. }
  254. Old = State.data;
  255. }else{
  256. console.log(State.data,Old);
  257. console.warn('Stopped double load of '+Old.type+'-'+Old.id);
  258. loading(false);
  259. }
  260. },
  261. equal = function(o1,o2){
  262. for(var i in o1){
  263. if(!exists(o2[i])||o2[i]!=o1[i]){
  264. return false;
  265. }
  266. }
  267. for(i in o2){
  268. if(!exists(o1[i])||o2[i]!=o1[i]){
  269. return false;
  270. }
  271. }
  272. return true;
  273. },
  274. render = window.render = {
  275. topbar: function(t,c){
  276. $('#topbar').html(Handlebars.compile(t)(c));
  277. render.links('#topbar');
  278. render.buttons('#topbar');
  279. render.menus('#topbar');
  280. if(State.url == location.origin+'/page-index'){
  281. $('#topbar').find('.topbar-history').hide();
  282. }
  283. $('#topbar').addClass('overflow-hide');
  284. $(window).resize();
  285. },
  286. content: function(t,c){
  287. $('#content').html(
  288. Handlebars.compile(t)(c)
  289. );
  290. render.links('#content');
  291. render.buttons('#content');
  292. render.accordions('#content');
  293. render.menus('#content');
  294. render.form('#content');
  295. $(window).resize();
  296. },
  297. accordions: function(selector){
  298. $(selector).find('.accordion').each(function(){
  299. var icons = {};
  300. if($(this).children('.icons').length == 1){
  301. icons = JSON.parse($(this).children('.icons').text());
  302. }
  303. $(this).children('.icons').remove();
  304. $(this).accordion({
  305. collapsible: true,
  306. icons: icons,
  307. active: false
  308. });
  309. });
  310. },
  311. buttons: function(selector){
  312. $(selector).find('.button').button();
  313. $(selector).find('input[type=submit]').button();
  314. $(selector).find('input[type=button]').button();
  315. $(selector).find('button').button();
  316. },
  317. menus: function(selector){
  318. $(selector).find('.menu').css({
  319. 'list-style':'none'
  320. }).menu({
  321. icons:{
  322. submenu: "ui-icon-circle-triangle-e"
  323. }
  324. }).removeClass('ui-corner-all').addClass('ui-corner-bottom').parent().click(function(e){
  325. e.stopPropagation();
  326. });
  327. },
  328. form: function(selector){
  329. $(selector).find('#form').position({of:selector,my:'center',at:'center'});
  330. },
  331. dialog: function(selector){
  332. $(selector).dialog({
  333. close: function(){
  334. loading(false);
  335. flag('error',false);
  336. },
  337. resizable: false,
  338. draggable: false
  339. });
  340. },
  341. links: function(selector){
  342. $(selector).find('a').each(function(){
  343. var href = this.href;
  344. if(href.indexOf('#')!=-1&&(href.indexOf(location.origin)!=-1||href.indexOf('#')==0)){
  345. href = href.substr(href.indexOf('#')+1);
  346. $(this).click(function(e){
  347. try{
  348. if(($(this).hasClass('topbar-home') || $(this).hasClass('topbar-back'))&&$(window).width()<767){
  349. $('#topbar').children('div.topbar-right,div.topbar-left').toggle();
  350. $('#topbar').toggleClass('overflow-hide');
  351. $(window).resize();
  352. }else if($(this).hasClass('topbar-history')){
  353. back();
  354. }else{
  355. loadState(href);
  356. }
  357. }catch(error){
  358. console.error(error);
  359. }
  360. e.preventDefault();
  361. return false;
  362. });
  363. }
  364. });
  365. }
  366. },
  367. back = function(){
  368. if(exists(State.data.back)){
  369. if(!History.back()){
  370. loadState(State.data.back);
  371. }
  372. }else if(State.data.type != 'page' || State.data.id != 'index'){
  373. loadState('page-index');
  374. }else{
  375. location.reload();
  376. }
  377. },
  378. alert = function(text){
  379. $('#dialog').text(text);
  380. render.dialog('#dialog');
  381. },
  382. loading = function(state){
  383. state = exists(state)?state:false;
  384. console.log('loading state '+state);
  385. if(!flag('error') && !state){
  386. $('#loading').hide();
  387. }else if(state){
  388. $('#loading').show();
  389. }
  390. };
  391. if(exists($.cookie('key'))){
  392. setKey($.cookie('key'));
  393. }else{
  394. setKey(null);
  395. }
  396. $(document).ready(function(){
  397. $.fn.serializeObject = function(){
  398. var o = {},
  399. a = this.serializeArray();
  400. $.each(a,function(){
  401. if(o[this.name] !== undefined){
  402. if(!o[this.name].push){
  403. o[this.name] = [o[this.name]];
  404. }
  405. o[this.name].push(this.value || '');
  406. }else{
  407. o[this.name] = this.value || '';
  408. }
  409. });
  410. return o;
  411. };
  412. $.ajaxSetup({
  413. async: false,
  414. cache: false,
  415. timeout: 2000
  416. });
  417. $(document).ajaxError(function(event, request, settings) {
  418. error({error:'Request timed out'});
  419. });
  420. templates = $.localStorage('templates');
  421. if(templates === null){
  422. templates = [];
  423. }
  424. if(!exists($.support.touch)){
  425. $.support.touch = 'ontouchstart' in window || 'onmsgesturechange' in window;
  426. }
  427. $('#content').niceScroll({
  428. cursorwidth: 10,
  429. nativeparentscrolling: false,
  430. preservenativescrolling: false
  431. });
  432. $('#content,#topbar').click(function(){
  433. $('.menu').hide();
  434. });
  435. document.addEventListener('touchmove',function(e){
  436. e.preventDefault();
  437. });
  438. $(window).resize(function(){
  439. if($(window).width()>767){
  440. $('#topbar div.topbar-right, #topbar div.topbar-left').css({
  441. 'display': ''
  442. });
  443. }
  444. $('#content').height($('body').height()-$('#topbar').height());
  445. $('#content').getNiceScroll().resize();
  446. render.form('#content');
  447. });
  448. var data = {
  449. get: 'settings',
  450. timestamp: +new Date,
  451. back: false
  452. };
  453. $.get(location.href,data,function(d){
  454. settings = d;
  455. apiState(location.href);
  456. },'json');
  457. $(window).on('statechange',function(){
  458. if(!flag('handled')){
  459. console.log('unhandled state change event');
  460. stateChange();
  461. }
  462. });
  463. var getState = History.getState;
  464. History.getState = function(flag){
  465. if(exists(flag)){
  466. return State;
  467. }else{
  468. return getState.call(History);
  469. }
  470. };
  471. });
  472. shortcut.add('f12',function(){
  473. if(!flag('firebug-lite')){
  474. $('head').append(
  475. $('<script>').attr({
  476. 'type': 'text/javascript',
  477. 'src': 'https://getfirebug.com/firebug-lite.js#startOpened',
  478. 'id': 'FirebugLite'
  479. })
  480. );
  481. $('<image>').attr('src','https://getfirebug.com/releases/lite/latest/skin/xp/sprite.png');
  482. flag('firebug-lite',true);
  483. }
  484. });
  485. shortcut.add('Ctrl+f12',function(){
  486. if(!flag('manifesto')){
  487. if(window.applicationCache){
  488. if(window.applicationCache.status==window.applicationCache.UNCACHED){
  489. $('head').append(
  490. $('<script>').attr({
  491. 'type': 'text/javascript',
  492. 'src': 'http://manifesto.ericdelabar.com/manifesto.js?x="+(Math.random())'
  493. })
  494. );
  495. (function wait(){
  496. if($('#cacheStatus').length == 0){
  497. setTimeout(wait,10);
  498. }else{
  499. $('#cacheStatus').niceScroll();
  500. }
  501. })();
  502. }else{
  503. alert("Manifest file is valid.");
  504. }
  505. }else{
  506. alert("This browser does not support HTML5 Offline Application Cache.");
  507. }
  508. flag('manifesto',true);
  509. }
  510. });
  511. shortcut.add('Shift+f12',function(){
  512. templates = [];
  513. $.localStorage('templates',null);
  514. console.log('Templates cleared.');
  515. });
  516. })(jQuery,History,console);