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