index.js 11 KB

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