main.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. /*
  2. This is the core logic for the main page.
  3. It implements most page transitions by showing and hiding DIV elements
  4. in the page with javascript+jquery
  5. */
  6. /* Convert a JSON string to an object, or null if unparseable */
  7. function j2o(json) { try { return JSON.parse(json); } catch(e) { return null; } }
  8. /* Convert an object to a JSON string (just easier to type than "JSON.stringify" */
  9. function o2j(obj) { return JSON.stringify(obj); }
  10. var user = {};
  11. var router = {
  12. routes: {},
  13. add: function(name, useAjax, cb) {
  14. if (typeof useAjax === 'function') {
  15. cb = useAjax;
  16. useAjax = true;
  17. }
  18. this.routes[name] = {
  19. fn: cb,
  20. useAjax: useAjax
  21. }
  22. },
  23. run: function(name, path) {
  24. $('.nav').removeClass('active');
  25. checkUser(function() {
  26. if (router.routes[name].useAjax) {
  27. $.get(path, {cache: false}, function(data) {
  28. if (data.status === 'not_found' || (typeof data === 'string')) {
  29. return router.run('404');
  30. }
  31. router.routes[name].fn(data, render);
  32. });
  33. } else {
  34. router.routes[name].fn(render);
  35. }
  36. });
  37. }
  38. }
  39. function render(pageId, response) {
  40. if (user.name) {
  41. $('.username').text("Hi, "+user.name+"!");
  42. $("#login_status").show();
  43. $('#login_link').text('Logout').attr('href', '/logout');
  44. $('#register_link').hide();
  45. $('#profile_link').show();
  46. } else {
  47. $('.username').text('Guest');
  48. $("#login_status").hide();
  49. $('#login_link').text('Login').attr('href', '/login');
  50. $('#register_link').show();
  51. $('#profile_link').hide();
  52. }
  53. if (response) {
  54. if (response instanceof Array) {
  55. $.each(response, function() {
  56. ProtoDiv.reset("PROTO_" + this.id)
  57. ProtoDiv.replicate("PROTO_" + this.id, this.data)
  58. })
  59. } else {
  60. ProtoDiv.reset("PROTO_" + response.id)
  61. ProtoDiv.replicate("PROTO_" + response.id, response.data)
  62. }
  63. }
  64. $("#pg_" + pageId).fadeIn(100);
  65. }
  66. function message(type, msg) {
  67. ProtoDiv.reset("PROTO_message");
  68. ProtoDiv.replicate("PROTO_message", {type: type, msg: msg})
  69. $("#messages").fadeIn(100);
  70. }
  71. function checkUser(cb) {
  72. $.get('/checkuser', function(data) {
  73. if (data.user) {
  74. user = data.user;
  75. } else {
  76. user = {};
  77. }
  78. if (cb) {
  79. cb();
  80. }
  81. })
  82. }
  83. router.add('404', false, function() {
  84. $("#pg_notfound").fadeIn(100);
  85. window.scroll(0, 0)
  86. });
  87. router.add('home', false, function(cb) {
  88. $('#learnsomething').unbind();
  89. $('.nav').removeClass('active');
  90. cb("home");
  91. $('#learnsomething').click(function(e) {
  92. $.get('/learn/random', function(data) {
  93. if (data.status === 'ok') {
  94. goPage(data.data);
  95. }
  96. })
  97. });
  98. if ($('#vimeo-screencast').length === 0) {
  99. $('.video-wrapper').html('<iframe id="vimeo-screencast" src="http://player.vimeo.com/video/30647271?title=0&amp;byline=0&amp;portrait=0&amp;color=367da9" width="460" height="259" frameborder="0" webkitAllowFullScreen allowFullScreen></iframe>');
  100. }
  101. });
  102. // go to the page that lists the schools
  103. router.add('schools', function(data, cb) {
  104. $('#school_link').addClass('active');
  105. var response = {
  106. id: 'school',
  107. data: data.schools
  108. }
  109. cb("schools", response);
  110. });
  111. // go to the page that lists the courses for a specific school
  112. router.add('school', function(data, cb) {
  113. $('#school_link').addClass('active');
  114. $('.sub_menu').hide();
  115. $('#new_course').unbind();
  116. $('#form_course').hide().unbind();
  117. var response = {
  118. id: 'course',
  119. data: data.school.courses
  120. }
  121. $("#school_name").html(data.school.name);
  122. if (data.school.authorized) {
  123. $('.sub_menu').show();
  124. $('#new_course').click(function(e) {
  125. e.preventDefault();
  126. var form = $('#form_course');
  127. form.toggle();
  128. form.submit(function(e) {
  129. e.preventDefault();
  130. $.post(window.location.pathname, form.serialize(), function(data) {
  131. if (data.status === 'error') {
  132. message('error', data.message);
  133. } else if (data.status === 'ok') {
  134. form.hide();
  135. goPage(window.location.pathname);
  136. message('info', data.message);
  137. }
  138. });
  139. })
  140. });
  141. }
  142. cb("courses", response)
  143. });
  144. // go to the page that lists the lectures for a specific course
  145. router.add('course', function(data, cb) {
  146. $('#school_link').addClass('active');
  147. $('.sub_menu').hide();
  148. $('#new_lecture').unbind();
  149. $('#form_lecture').hide().unbind();;
  150. var response = [];
  151. if (data.course) {
  152. response.push({
  153. id: 'lectures_head',
  154. data: data.course
  155. })
  156. }
  157. if (data.instructor) {
  158. response.push({
  159. id: 'lectures_instructor',
  160. data: data.instructor
  161. })
  162. }
  163. if (data.lectures) {
  164. response.push({
  165. id: 'lecture',
  166. data: data.lectures.map(function(lecture) {
  167. var date = new Date(lecture.date);
  168. lecture.date = date.toDateString();
  169. return lecture;
  170. })
  171. })
  172. }
  173. cb('lectures', response);
  174. if (!data.instructor.email) {
  175. $('.instructor_email').hide();
  176. } else {
  177. $('.instructor_email').show();
  178. }
  179. if (data.course.authorized) {
  180. $('.sub_menu').show();
  181. $('#new_lecture').click(function(e) {
  182. e.preventDefault();
  183. var form = $('#form_lecture');
  184. form.toggle();
  185. form.submit(function(e) {
  186. e.preventDefault();
  187. $.post(window.location.pathname, form.serialize(), function(data) {
  188. if (data.status === 'error') {
  189. message('error', data.message);
  190. } else if (data.status === 'ok') {
  191. form.hide();
  192. goPage(window.location.pathname);
  193. message('info', data.message);
  194. }
  195. });
  196. })
  197. });
  198. }
  199. });
  200. // go to the page that lists the note taking sessions for a specific lecture
  201. router.add('lecture', function(data, cb) {
  202. $('#school_link').addClass('active');
  203. $('.sub_menu').hide();
  204. $('#new_note').unbind();
  205. $('#form_note').hide().unbind();;
  206. var response = [];
  207. if (data.course) {
  208. response.push({
  209. id: 'notes_head',
  210. data: data.course
  211. })
  212. }
  213. if (data.instructor) {
  214. response.push({
  215. id: 'notes_instructor',
  216. data: data.instructor
  217. })
  218. }
  219. if (data.notes) {
  220. response.push({
  221. id: 'note',
  222. data: data.notes
  223. })
  224. }
  225. cb("notes", response);
  226. if (!data.instructor.email) {
  227. $('.instructor_email').hide();
  228. } else {
  229. $('.instructor_email').show();
  230. }
  231. if (data.lecture.authorized) {
  232. $('.sub_menu').show();
  233. $('#new_note').click(function(e) {
  234. e.preventDefault();
  235. var form = $('#form_note');
  236. form.toggle();
  237. form.submit(function(e) {
  238. e.preventDefault();
  239. $.post(window.location.pathname, form.serialize(), function(data) {
  240. if (data.status === 'error') {
  241. message('error', data.message);
  242. } else if (data.status === 'ok') {
  243. form.hide();
  244. goPage(window.location.pathname);
  245. message('info', data.message);
  246. }
  247. });
  248. })
  249. });
  250. }
  251. });
  252. // go to the page that lists the archived subject names
  253. router.add('archive', function(data, cb) {
  254. $('#archive_link').addClass('active');
  255. var response = {
  256. id: 'archive_subject',
  257. data: data.subjects
  258. }
  259. cb("archive_subjects", response)
  260. });
  261. router.add('archivesubject', function(data, cb) {
  262. $('.nav').removeClass('active');
  263. $('#archive_link').addClass('active');
  264. var response = {
  265. id: 'archive_course',
  266. data: data.courses
  267. }
  268. cb("archive_courses", response)
  269. });
  270. router.add('archivecourse', function(data, cb) {
  271. $('#archive_link').addClass('active');
  272. var response = {
  273. id: 'archive_note',
  274. data: data.notes
  275. }
  276. cb("archive_notes", response)
  277. });
  278. router.add('archivenote', function(data, cb) {
  279. $('#archive_link').addClass('active');
  280. var response = {
  281. id: 'archive_note_display',
  282. data: data.note
  283. }
  284. cb("archive_note_display", response)
  285. });
  286. // go to the account registration page
  287. router.add('register', false, function(cb) {
  288. $('#register_link').addClass('active');
  289. $('#form_register').submit(function(e) {
  290. e.preventDefault();
  291. var form = $(this);
  292. $.post(window.location.pathname, form.serialize(), function(data) {
  293. if (data.status === 'error') {
  294. message('error', data.message);
  295. return false;
  296. } else if (data.status === 'ok') {
  297. goPage('/')
  298. message('info', data.message);
  299. }
  300. })
  301. })
  302. cb("register");
  303. });
  304. router.add('activate', function(data, cb) {
  305. goPage('/')
  306. message('info', data.message);
  307. });
  308. router.add('profile', false, function(cb) {
  309. $('#profile_link').addClass('active');
  310. var form = $('#form_profile');
  311. $('input[type=password]','#form_profile').val('');
  312. $('#affiliation').attr('value', user.affil);
  313. $('#showName').attr('checked', user.showName)
  314. form.find('.email').text(user.email);
  315. form.find('input[name=name]').val(user.name);
  316. form.submit(function(e) {
  317. e.preventDefault();
  318. $.post(window.location.pathname, form.serialize(), function(data) {
  319. if (data.status === 'error') {
  320. message('error', data.message);
  321. return false;
  322. } else if (data.status === 'ok') {
  323. goPage('/profile');
  324. message('info', data.message);
  325. }
  326. })
  327. })
  328. cb("profile");
  329. });
  330. router.add('login', false, function(cb) {
  331. $('input','#form_login').val('');
  332. $('#form_login').submit(function(e) {
  333. e.preventDefault();
  334. var form = $(this);
  335. $.post(window.location.pathname, form.serialize(), function(data) {
  336. if (data.status === 'error') {
  337. message('error', data.message);
  338. return false;
  339. } else if (data.status === 'ok') {
  340. goPage('/')
  341. message('info', 'Successfully logged in');
  342. }
  343. })
  344. })
  345. cb("login");
  346. });
  347. router.add('logout', function(data, cb) {
  348. goPage('/')
  349. message('info', 'Successfully logged out');
  350. });
  351. router.add('resetpass', false, function(cb) {
  352. $('input','#form_resetpass').val('');
  353. $('#form_resetpass').submit(function(e) {
  354. e.preventDefault();
  355. var form = $(this);
  356. $.post(window.location.pathname, form.serialize(), function(data) {
  357. if (data.status === 'error') {
  358. message('error', data.message);
  359. return false;
  360. } else if (data.status === 'ok') {
  361. goPage('/')
  362. message('info', data.message);
  363. }
  364. })
  365. })
  366. cb("resetpass");
  367. });
  368. router.add('resetpw', false, function(cb) {
  369. $('input','#form_resetpw').val('');
  370. $('#form_resetpw').submit(function(e) {
  371. e.preventDefault();
  372. var form = $(this);
  373. $.post(window.location.pathname, form.serialize(), function(data) {
  374. if (data.status === 'error') {
  375. message('error', data.message);
  376. return false;
  377. } else if (data.status === 'ok') {
  378. goPage('/')
  379. message('info', data.message);
  380. }
  381. })
  382. })
  383. cb("resetpw");
  384. });
  385. // go to the press articles page
  386. router.add('press', false, function(cb) {
  387. $('#press_link').addClass('active');
  388. cb("press");
  389. });
  390. // go to the "code of conduct" page
  391. router.add('conduct', false, function(cb) {
  392. cb("conduct");
  393. });
  394. /* Do and show the appropriate thing, based on the pages current URL */
  395. function showPage(y) {
  396. $(".page").hide(); //(100); // hide all pseudo pages
  397. var path = document.location.pathname
  398. var routes = router.routes;
  399. var slugs = path.split('/');
  400. slugs.shift();
  401. mainSlug = slugs[0].toLowerCase() || 'home';
  402. if (mainSlug === 'archive') {
  403. if (slugs[1]) {
  404. mainSlug = mainSlug + slugs[1];
  405. }
  406. }
  407. if (routes[mainSlug]) {
  408. router.run(mainSlug, path)
  409. } else {
  410. router.run('404')
  411. }
  412. }
  413. /* Simulates a page load.
  414. 'path' is something like "/schools", etc.
  415. A page fetch doesn't really happen.
  416. Based on what path looks like, an appropriate DIV is shown, and action taken
  417. */
  418. var topQueue = [0]
  419. function goPage(path) {
  420. if (history.pushState !== undefined) {
  421. topQueue.push(window.pageYOffset)
  422. history.pushState({}, path, path);
  423. showPage(0);
  424. } else {
  425. document.location = path;
  426. }
  427. }
  428. /* Simulates a "back" browser navigation. */
  429. var popped = false;
  430. function goBack(event) {
  431. popped = true;
  432. console.timeEnd('pop')
  433. showPage( topQueue.pop() );
  434. }
  435. console.time('pop')
  436. console.time('no-pop')
  437. window.onpopstate = goBack
  438. $(document).ready(function() {
  439. // This code executes after the page has been fully loaded
  440. $('body').on('click', 'a[href^=/]', function(e) {
  441. var path = e.target.pathname || '/';
  442. var checkNote = path.match(/\/([a-zA-Z]+)/);
  443. if (checkNote && checkNote[1] == 'note') {
  444. return true;
  445. } else if (!history.pushState) {
  446. return true;
  447. } else {
  448. goPage(path)
  449. return false;
  450. }
  451. })
  452. // xxx older FF browsers don't fire a page load/reload - deal with it somehow.
  453. setTimeout(function() {
  454. console.timeEnd('no-pop')
  455. if (!popped) {
  456. showPage( 0 ); // needed for some older browsers, redundant for chrome
  457. }
  458. }, 200)
  459. })