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