index.js 14 KB

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