Checking in changes made in-place on the server to be merged
[oweals/finalsclub.git] / public / javascripts / main.js
1
2 /*
3
4 This is the core logic for the main page.
5 It implements most page transitions by showing and hiding DIV elements
6 in the page with javascript+jquery
7
8 */
9
10
11 /* Convert a JSON string to an object, or null if unparseable */
12 function j2o(json) { try { return JSON.parse(json); } catch(e) { return null; } }
13
14 /* Convert an object to a JSON string (just easier to type than "JSON.stringify" */
15 function o2j(obj) { return JSON.stringify(obj); }
16
17 var
18 user = {},
19
20 router = {
21
22     routes: {},
23
24     add: function(name, useAjax, cb) {
25         if (typeof useAjax === 'function') {
26             cb = useAjax;
27             useAjax = true;
28         }
29
30         this.routes[name] = {
31             fn: cb,
32             useAjax: useAjax
33         }
34     },
35
36     run: function(name, path) {
37
38         $('.nav').removeClass('active');
39
40         checkUser( function() {
41
42             if (router.routes[name].useAjax) {
43
44                 $.get(
45                     path,
46                     {cache: false},
47                     function(data) {
48
49                         if (data.status === 'not_found' || (typeof data === 'string')) {
50                             return router.run('404');
51                         }
52                         router.routes[name].fn(data, render);
53                     }
54                 );
55             } else {
56                 router.routes[name].fn(render);
57             }
58         });
59     }
60 }
61
62 function tmpl_render() {
63   if (user.name) {
64     $('.username').text(user.name).attr('href', '/profile');
65     $("#login_status").show();
66     $('#login_link').text('Logout').attr('href', '/logout');
67     $('#register_link').hide();
68                 $('#sign_up-link').hide();
69   } else {
70     $('.username').text('Guest');
71     $("#login_status").hide();
72     $('#login_link').text('Login').attr('href', '/login');
73     $('#register_link').show();
74                 $('#sign_up-link').show();
75   }
76 };
77
78 function render(pageId, response) {
79   console.log(user);
80   if (user.name) {
81     $('.username').text(user.name).attr('href', '/profile');
82     $("#login_status").show();
83     $('#login_link').text('Logout').attr('href', '/logout');
84     $('#register_link').hide();
85                 $('#sign_up-link').hide();
86   } else {
87     $('.username').text('Guest');
88     $("#login_status").hide();
89     $('#login_link').text('Login').attr('href', '/login');
90     $('#register_link').show();
91                 $('#sign_up-link').show();
92   }
93
94   if (response) {
95     if (response instanceof Array) {
96       $.each(response, function() {
97         ProtoDiv.reset("PROTO_" + this.id)
98         ProtoDiv.replicate("PROTO_" + this.id, this.data)
99       })
100     } else {
101       ProtoDiv.reset("PROTO_" + response.id)
102       ProtoDiv.replicate("PROTO_" + response.id, response.data)
103     }
104   }
105         $("#pg_" + pageId).fadeIn(function() {
106                 $("#g-footer").fadeIn(); // we don't want the footer jumping up and down
107         });
108 }
109
110 function message(type, msg) {
111   ProtoDiv.reset("PROTO_message");
112   ProtoDiv.replicate("PROTO_message", {type: type, msg: msg})
113   $("#messages").fadeIn(100);
114 }
115
116 function checkUser(cb) {
117   $.get('/checkuser', function(data) {
118     console.log(data);
119     if (data.user) {
120       user = data.user;
121     } else {
122       user = {};
123     }
124
125     if (cb) {
126       cb();
127     }
128   })
129 }
130
131 router.add('404', false, function() {
132   $("#pg_notfound").fadeIn(100);
133   window.scroll(0, 0)
134 });
135
136 // Legal page
137 router.add('legal', false, function() {
138   $("#pg_legal").fadeIn(100);
139 });
140
141
142 // Team Page
143 router.add('team', false, function() {
144   $("#pg_team").fadeIn(100);
145 });
146
147 // Contact Page
148
149 router.add('contact', false, function() {
150   $("#pg_contact").fadeIn(100);
151 });
152
153 // About Page
154 router.add('about', false, function() {
155   $("#pg_about").fadeIn(100);
156 });
157
158 router.add('home', false, function(cb) {
159   $('#learnsomething').unbind();
160   $('.nav').removeClass('active');
161         cb("home");
162   $('#signup').click(function(e) {
163     goPage('/register');
164   });
165   $('#learnsomething').click(function(e) {
166     $.get('/learn/random', function(data) {
167       if (data.status === 'ok') {
168         goPage(data.data);
169       }
170     })
171   });
172   if ($('#vimeo-screencast').length === 0) {
173     $('.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="400" height="225" frameborder="0" webkitAllowFullScreen allowFullScreen></iframe>');
174   }
175 });
176
177 // go to the page that lists the schools
178 router.add('schools', function(data, cb) {
179
180   $('#school_link').addClass('active');
181   tmpl_render();
182
183   var response = {
184     id: 'school',
185     data: data.schools
186   }
187
188   $('#pg_schools').fadeIn(function() {
189       $('#g-footer').fadeIn();
190   });
191   $('#schoolTmpl').tmpl( data.schools ).appendTo("#pg_schools #schools");
192 });
193
194 // go to the page that lists the courses for a specific school
195 router.add('school', function(data, cb) {
196   $('#school_link').addClass('active');
197   $('.sub_menu').hide();
198   //$('#new_course').unbind();
199   $('#form_course').hide().unbind();
200
201   response = {
202     id: 'course',
203     data: data.school.courses
204   }
205
206   $('#pg_courses').fadeIn(function() {
207       $('#g-footer').fadeIn();
208   });
209   $("#school_name").html(data.school.name);
210
211   if (data.school.authorized) {
212     $('.sub_menu').show();
213     var form = $('#form_course');
214
215     form.toggle();
216
217     form.submit(function(e) {
218       e.preventDefault();
219
220       $.post(window.location.pathname, form.serialize(), function(data) {
221         if (data.status === 'error') {
222           message('error', data.message);
223         } else if (data.status === 'ok') {
224           form.hide();
225           goPage(window.location.pathname);
226           message('info', data.message);
227         }
228       });
229     })
230   }
231
232   $('#courseTmpl').tmpl( data.school.courses ).appendTo("#pg_courses #main-content");
233 });
234
235 // go to the page that lists the lectures for a specific course
236 router.add('course', function(data, cb) {
237   $('#school_link').addClass('active');
238   $('.sub_menu').hide();
239   $('#new_lecture').unbind();
240
241   var response = [];
242
243   if (data.course) {
244     response.push({
245       id: 'lectures_head',
246       data: data.course
247     })
248   }
249
250   if (data.instructor) {
251     response.push({
252       id: 'lectures_instructor',
253       data: data.instructor
254     })
255   }
256
257   if (data.lectures) {
258     response.push({
259       id: 'lecture',
260       data: data.lectures.map(function(lecture) {
261         var date = new Date(lecture.date);
262         lecture.date = date.toDateString();
263         return lecture;
264       })
265     })
266   }
267   cb('lectures', response);
268
269   if (!data.instructor.email) {
270     $('.instructor_email').hide();
271   } else {
272     $('.instructor_email').show();
273   }
274
275   if (data.course.authorized) {
276     $('.sub_menu').show();
277                 var form = $('#form_lecture');
278
279                 form.show();
280
281                 form.submit(function(e) {
282                         e.preventDefault();
283
284                         $.post(window.location.pathname, form.serialize(), function(data) {
285                                 if (data.status === 'error') {
286                                         message('error', data.message);
287                                 } else if (data.status === 'ok') {
288                                         form.hide();
289                                         goPage(window.location.pathname);
290                                         message('info', data.message);
291                                 }
292                         });
293                 })
294   }
295 });
296
297
298
299 // go to the page that lists the note taking sessions for a specific lecture
300 router.add('lecture', function(data, cb) {
301   $('#school_link').addClass('active');
302   $('.sub_menu').hide();
303   $('#new_note').unbind();
304   $('#form_note').hide().unbind();;
305
306   var response = [];
307
308   if (data.course) {
309     response.push({
310       id: 'notes_head',
311       data: data.course
312     })
313   }
314
315   if (data.instructor) {
316     response.push({
317       id: 'notes_instructor',
318       data: data.instructor
319     })
320   }
321
322   if (data.notes) {
323     response.push({
324       id: 'note',
325       data: data.notes
326     })
327   }
328   
329   cb("notes", response);
330
331   if (!data.instructor.email) {
332     $('.instructor_email').hide();
333   } else {
334     $('.instructor_email').show();
335   }
336   
337   if (data.lecture.authorized) {
338
339                 var form = $('#form_note').show();
340
341                 form.submit(function(e) {
342                         e.preventDefault();
343
344                         $.post(window.location.pathname, form.serialize(), function(data) {
345                                 if (data.status === 'error') {
346                                         message('error', data.message);
347                                 } else if (data.status === 'ok') {
348                                         form.hide();
349                                         goPage(window.location.pathname);
350                                         message('info', data.message);
351                                 }
352                         });
353                 })
354   }
355 });
356
357
358 // go to the page that lists the archived subject names
359 router.add('archive', function(data, cb) {
360   $('#archive_link').addClass('active');
361
362   var response = {
363     id: 'archive_subject',
364     data: data.subjects
365   }
366
367   cb("archive_subjects", response)
368 });
369
370
371
372 router.add('archivesubject', function(data, cb) {
373   $('.nav').removeClass('active');
374   $('#archive_link').addClass('active');
375
376   var response = {
377     id: 'archive_course',
378     data: data.courses
379   }
380
381   cb("archive_courses", response)
382 });
383
384
385
386 router.add('archivecourse', function(data, cb) {
387   $('#archive_link').addClass('active');
388
389   var response = {
390     id: 'archive_note',
391     data: data.notes
392   }
393
394   cb("archive_notes", response)
395 });
396
397
398
399 router.add('archivenote', function(data, cb) {
400   $('#archive_link').addClass('active');
401
402   var response = {
403     id: 'archive_note_display',
404     data: data.note
405   }
406
407   cb("archive_note_display", response)
408 });
409
410
411
412 // go to the account registration page
413 router.add('register', false, function(cb) {
414   $('#register_link').addClass('active');
415   $('#form_register').submit(function(e) {
416     e.preventDefault();
417
418     var form = $(this);
419
420     $.post(window.location.pathname, form.serialize(), function(data) {
421       if (data.status === 'error') {
422         message('error', data.message);
423         return false;
424       } else if (data.status === 'ok') {
425         goPage('/')
426         message('info', data.message);
427       }
428     })
429   })
430         cb("register");
431 });
432
433 router.add('activate', function(data, cb) {
434   goPage('/')
435   message('info', data.message);
436 });
437
438 router.add('profile', false, function(cb) {
439   $('#profile_link').addClass('active');
440   var form = $('#form_profile');
441   $('input[type=password]','#form_profile').val('');
442   $('#affiliation').attr('value', user.affil);
443   $('#showName').attr('checked', user.showName)
444   form.find('.email').text(user.email);
445   form.find('input[name=name]').val(user.name);
446   form.submit(function(e) {
447     e.preventDefault();
448
449     $.post(window.location.pathname, form.serialize(), function(data) {
450       if (data.status === 'error') {
451         message('error', data.message);
452         return false;
453       } else if (data.status === 'ok') {
454         goPage('/profile');
455         message('info', data.message);
456       }
457     })
458   })
459         cb("profile");
460 });
461
462 router.add('login', false, function(cb) {
463   $('input','#form_login').val('');
464   $('#form_login').submit(function(e) {
465     e.preventDefault();
466
467     var form = $(this);
468
469     $.post(window.location.pathname, form.serialize(), function(data) {
470       if (data.status === 'error') {
471         message('error', data.message);
472         return false;
473       } else if (data.status === 'ok') {
474         goPage('/')
475         message('info', 'Successfully logged in');
476       }
477     })
478   })
479         cb("login");
480 });
481
482 router.add('logout', function(data, cb) {
483   goPage('/')
484   message('info', 'Successfully logged out');
485 });
486
487 router.add('resetpass', false, function(cb) {
488   $('input','#form_resetpass').val('');
489   $('#form_resetpass').submit(function(e) {
490     e.preventDefault();
491
492     var form = $(this);
493
494     $.post(window.location.pathname, form.serialize(), function(data) {
495       if (data.status === 'error') {
496         message('error', data.message);
497         return false;
498       } else if (data.status === 'ok') {
499         goPage('/')
500         message('info', data.message);
501       }
502     })
503   })
504         cb("resetpass");
505 });
506
507 router.add('resetpw', false, function(cb) {
508   $('input','#form_resetpw').val('');
509   $('#form_resetpw').submit(function(e) {
510     e.preventDefault();
511
512     var form = $(this);
513
514     $.post(window.location.pathname, form.serialize(), function(data) {
515       if (data.status === 'error') {
516         message('error', data.message);
517         return false;
518       } else if (data.status === 'ok') {
519         goPage('/')
520         message('info', data.message);
521       }
522     })
523   })
524         cb("resetpw");
525 });
526
527 // go to the press articles page
528 router.add('press', false, function(cb) {
529   $('#press_link').addClass('active');
530         cb("press");
531 });
532
533
534 // go to the "code of conduct" page
535 router.add('conduct', false, function(cb) {
536         cb("conduct");
537 });
538
539
540
541 /* Do and show the appropriate thing, based on the pages current URL */
542 function showPage(y) {
543
544     $('.page').hide(); //(100);// hide all pseudo pages
545                 $('#g-footer').hide();
546
547     var
548     path = document.location.pathname,
549     routes = router.routes,
550     slugs = path.split('/');
551
552     slugs.shift();
553
554     var mainSlug = slugs[0].toLowerCase() || 'home';
555                 console.log(slugs[0].toLowerCase());
556
557     if (mainSlug === 'archive') {
558         if (slugs[1]) {
559             mainSlug = mainSlug + slugs[1];
560         }
561     }
562
563     if (routes[mainSlug]) {
564         router.run(mainSlug, path)
565     } else {
566         router.run('404')
567     }
568 }
569
570
571
572
573 /* Simulates a page load.
574         'path' is something like "/schools", etc.
575         A page fetch doesn't really happen.
576         Based on what path looks like, an appropriate DIV is shown, and action taken
577 */
578 var topQueue = [0]
579 function goPage(path) {
580   if (history.pushState !== undefined) {
581     topQueue.push(window.pageYOffset)
582     history.pushState({}, path, path);
583     showPage(0);
584   } else {
585     document.location = path;
586   }
587 }
588
589 /* Simulates a "back" browser navigation.  */
590 var popped = false;
591 function goBack(event) {
592   popped = true;
593   console.timeEnd('pop')
594         showPage( topQueue.pop() );
595 }
596
597 console.time('pop')
598 console.time('no-pop')
599 window.onpopstate = goBack
600
601 $(document).ready(function() {
602
603         // This code executes after the page has been fully loaded
604
605   $('body').on('click', 'a[href^=/]', function(e) {
606     var path = e.target.pathname || '/';
607     var checkNote = path.match(/\/([a-zA-Z]+)/);
608     if (checkNote && checkNote[1] == 'note') {
609       return true;
610     } else if (!history.pushState) {
611       return true;
612     } else {
613       goPage(path)
614       return false;
615     }
616   })
617
618     // xxx older FF browsers don't fire a page load/reload - deal with it somehow.
619     // I've increased the timeout, we need to avoid calling showPage twice. It causes page flicker.
620   setTimeout(function() {
621     console.timeEnd('no-pop')
622     if (!popped) {
623       showPage( 0 ); // needed for some older browsers, redundant for chrome
624     }
625   }, 1200)
626
627 })
628
629
630
631
632