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