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