Added instant message feature.
authorSteven Schronk <[email protected]>
Mon, 20 Aug 2012 03:35:09 +0000 (19 22:35 -0500)
committerSteven Schronk <[email protected]>
Mon, 20 Aug 2012 03:35:09 +0000 (19 22:35 -0500)
All users may message each other with an Ajax enabled message tool.

17 files changed:
TODO.TXT
ajax.js
auth.php
code_db.sql
gfx/bullet_green.png [new file with mode: 0644]
gfx/bullet_red.png [new file with mode: 0644]
gfx/bullet_yellow.png [new file with mode: 0644]
header.php
im.php [new file with mode: 0644]
im_chat_update.php [new file with mode: 0644]
im_send_message.php [new file with mode: 0644]
im_user_update.php [new file with mode: 0644]
manage.php
sfx/beep.wav [new file with mode: 0644]
sfx/click.wav [new file with mode: 0644]
sfx/doorknock.wav [new file with mode: 0644]
style.css

index e67e2f4..b372c0a 100644 (file)
--- a/TODO.TXT
+++ b/TODO.TXT
@@ -1,8 +1,32 @@
-cannot use "enter" key for login credentials form
 
-cannot recover password for students
+Cannot use "enter" key for login credentials form
+
+Users cannot recover password for themselves.
 
 Email for created or modified assignments
+
 Email for late assignments
+
 Email (to instructor) for comment from student
 
+Create management console that can look at all users's messages
+
+       Should include search to view only a particular user's chats.
+
+Allow users to see past chats anytime - everything is recorded
+
+Create private chat rooms between two people and a public room as well.
+
+Add sound for user entering chat and another for a user logging off.
+
+Create a message board for each assignment in system.
+
+Empty chat updates should not send any return at all - to reduce bandwidth.
+
+Noises made in chat are not what is needed.  Need a knock sound and a send and receive sound.
+
+Users in chat should have different color chats to make the messages more readable.
+
+Need a place for students and faculty to share code outside of assignments.
+
+Need logoff button instead of name to click on.
diff --git a/ajax.js b/ajax.js
index 2c48161..cab229a 100644 (file)
--- a/ajax.js
+++ b/ajax.js
@@ -28,8 +28,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 //                if waitMessage not sent, content will not "blink"
 // callBack = javascript function name to run after data has been returned from server
 function getPage( url, pageElement, waitMessage, callBack ) {
-
-               // START TIMER
                var timer = new Date();
                var t_start = timer.getTime();
                //debugEvent(url, "get");
@@ -96,3 +94,59 @@ function getPage( url, pageElement, waitMessage, callBack ) {
                }
        if (callBack != null ) { eval(callBack+'()'); }
 }
+
+function sendData(url){
+               // START TIMER
+               var timer = new Date();
+               var t_start = timer.getTime();
+// WE USE A JAVASCRIPT FEATURE HERE CALLED "INNER FUNCTIONS"
+// USING THESE MEANS THE LOCAL VARIABLES RETAIN THEIR VALUES AFTER THE OUTER FUNCTION
+// HAS RETURNED. THIS IS USEFUL FOR THREAD SAFETY, SO
+// REASSIGNING THE ONREADYSTATECHANGE FUNCTION DOESN'T STOMP OVER EARLIER REQUESTS.
+       function ajaxBindCallback(){
+               if(ajaxRequest.readyState == 0) { window.status = "Waiting...";      }
+               if(ajaxRequest.readyState == 1) { window.status = "Loading Page..."; }
+               if(ajaxRequest.readyState == 2) { window.status = "Data Received...";}
+               if(ajaxRequest.readyState == 3) { window.status = "Interactive...";  }
+               if(ajaxRequest.readyState == 4) {
+                       window.status = "Transaction Complete...";
+
+      // STOP TIMER AND FIND DIFFERENCE
+      // MUST CREATE NEW TIMER INSTANCE :)
+      var timer2 = new Date();
+      var t_end = timer2.getTime();
+      var t_diff = t_end - t_start;
+
+      // TEST HTTP STATUS CODE - DISPLAY IN DUBUGGER AND STATUS
+      switch (ajaxRequest.status.toString()) {
+        case "200" :
+          debugEvent(url, "got", ajaxRequest.responseText, t_diff);
+          break;
+        case "403" :
+          debugEvent(url, "error_403", ajaxRequest.responseText, t_diff);
+          break;
+        case "404" :
+          debugEvent(url, "error_404", ajaxRequest.responseText, t_diff);
+          break;
+        case "500" :
+          debugEvent(url, "error_500", ajaxRequest.responseText, t_diff);
+          break;
+        }
+                       }
+       }
+       var ajaxRequest = null;
+       // BIND OUR CALLBACK THEN HIT THE SERVER...
+       if (window.XMLHttpRequest) {
+               ajaxRequest = new XMLHttpRequest();
+               ajaxRequest.onreadystatechange = ajaxBindCallback;
+               ajaxRequest.open("GET", url, true);
+               ajaxRequest.send(null);
+       } else if (window.ActiveXObject) {
+               ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
+               if (ajaxRequest) {
+                       ajaxRequest.onreadystatechange = ajaxBindCallback;
+                       ajaxRequest.open("GET", url, true);
+                       ajaxRequest.send();
+                       }
+               }
+}
index 7ee9c03..8c9d9c7 100644 (file)
--- a/auth.php
+++ b/auth.php
@@ -1,5 +1,5 @@
 <?php
-
+ob_start("ob_gzhandler");
 include_once("conn.php");
 
 /* verify username and password - do not pass if incorrect */
@@ -47,6 +47,11 @@ $user_name = $row[3];
 $first_login = $row[4];
 $user_email = $row[5];
 
+/* update user status to indcate that this user is online - used mostly for chat features */
+$sql = "update users set last_click = NOW() where user_id = ".$user_id;
+
+$result = mysql_query($sql);
+
 /* if this is your first login, you MUST change password */
 if($first_login == 1) { include("password_change.php"); exit; }
 
index 4b109ec..1391dce 100644 (file)
@@ -105,6 +105,14 @@ CREATE TABLE files (
        PRIMARY KEY (file_id)
 );
 
+CREATE TABLE chat (
+       chat_id int NOT NULL AUTO_INCREMENT,
+       user_id int NOT NULL,
+       content varchar(256) NOT NULL,
+       chat_time timestamp NOT NULL,
+       PRIMARY KEY(chat_id)
+);
+
 CREATE TABLE users (
        user_id int NOT NULL AUTO_INCREMENT,            #
        email varchar(128) NOT NULL,                    # 
@@ -113,6 +121,7 @@ CREATE TABLE users (
        attempts int NOT NULL,                          # number of bad attempts to login
        role int NOT NULL,                              # 0 is prof, 1 is student
        first_login int NOT NULL,                       # 0 is false, 1 is true
+       last_click timestamp,
        PRIMARY KEY (user_id)
 );
 
diff --git a/gfx/bullet_green.png b/gfx/bullet_green.png
new file mode 100644 (file)
index 0000000..f74a914
Binary files /dev/null and b/gfx/bullet_green.png differ
diff --git a/gfx/bullet_red.png b/gfx/bullet_red.png
new file mode 100644 (file)
index 0000000..b6cfd0b
Binary files /dev/null and b/gfx/bullet_red.png differ
diff --git a/gfx/bullet_yellow.png b/gfx/bullet_yellow.png
new file mode 100644 (file)
index 0000000..604e614
Binary files /dev/null and b/gfx/bullet_yellow.png differ
index 0045c0f..ef4f732 100644 (file)
@@ -7,27 +7,26 @@ header("Cache-Control: post-check=0, pre-check=0", false);
 header("Pragma: no-cache");
 
 if($user_id != '') {
-       if($role == 0) { 
-               //$menu = '<a href="index.php">Classes</a> | <a href="manage.php">Manage Accounts</a> | <a href="workflow.php">Workflows</a> | '.$_COOKIE["username"].' | <a href="#" onClick="logout();">Logout</a>';
-
-               $menu = '<div class="menu">
-                       <ul>
-                               <li><a href="classes.php">Classes</a></li>
+       if($role == 0) {
+               $menu = '<ul>
+                               <li><a href="javascript:void(0);" target="_blank" onClick="window.open(\'im.php\', \'Chat Room\', \'toolbar=0,scrollbars=1,location=0,statusbar=0,menubar=0,resizable=0,width=700,height=400,left = 310,top = 275\');">Chat</a></li>
+                               <li><a href=#>Chat Log</a>
+                       <li><a href="classes.php">Classes</a></li>
+                               <li><a href="#">Discussion Board</a></li>
                                <li><a href="index.php">Messages</a></li>
-                               <li><a href="manage.php">Manage Accounts</a></li>
-                               <li><a href="#" onClick="logout();">'.$_COOKIE["username"].'</a></li>
-                       </ul></div>';
-
+                           <li><a href="manage.php">Manage Accounts</a></li>
+                               <li><a href="#">Search</a></li>
+                       </ul><div class="login_menu">'.$_COOKIE["username"].'&nbsp;&nbsp;&nbsp;&nbsp;<button class="logout_button" onClick="logout();">Logout</button></div>';
        } else {
-               //$menu = '<a href="index.php">Classes</a> | <a href="manage.php">Manage Account</a> | '.$_COOKIE["username"].' | <a href="#" onClick="logout();">Logout</a>';
-               $menu = '<div class="menu">
-                       <ul>
-                               <li><a href="classes.php">Classes</a></li>
+               $menu = '<ul>
+                               <li><a href="javascript:void(0);" target="_blank" onClick="window.open(\'im.php\', \'Chat Room\', \'toolbar=0,scrollbars=1,location=0,statusbar=0,menubar=0,resizable=0,width=700,height=400,left = 310,top = 275\');">Chat</a></li>
+                               <li><a href=#>Chat Log</a>
+                       <li><a href="classes.php">Classes</a></li>
+                               <li><a href="#">Discussion Board</a></li>
                                <li><a href="index.php">Messages</a></li>
-                               <li><a href="manage.php">Manage Account</a></li>
-                               <li><a href="#" onClick="logout();">'.$_COOKIE["username"].'</a></li>
-                       </ul></div>';
-
+                           <li><a href="manage.php">Manage Account</a></li>
+                               <li><a href="#">Search</a></li>
+                       </ul><div class="login_menu">'.$_COOKIE["username"].'&nbsp;&nbsp;&nbsp;&nbsp;<button class="logout_button" onClick="logout();">Logout</button></div>';
        }
 } else {
        $menu = '';
@@ -54,14 +53,7 @@ if($user_id != '') {
 </head>
 <body onload="sh_highlightDocument();">
 
-<table>        <tr><td><img src="gfx/bricks.png"></td><td class="banner_header">Assignment Trapper</td></tr></table>
-
-
-<div class="header">
-       <div class="menu">
-               <?php echo $menu; ?>
-       </div>
-</div>
-
+<div class="main_menu"><?php echo $menu; ?></div><br>
+<table><tr><td><img src="gfx/bricks.png"></td><td class="banner_header">Assignment Trapper</td></tr></table>
 <br>
 
diff --git a/im.php b/im.php
new file mode 100644 (file)
index 0000000..0bd7dcd
--- /dev/null
+++ b/im.php
@@ -0,0 +1,197 @@
+<?php
+
+include_once("auth.php");
+
+?>
+
+<html>
+<script type="text/javascript" src="ajax.js"></script>
+<script type="text/javascript" src="debugger.js"></script>
+<script>
+var last_chat_id = 0;
+var chat_div;
+var chat_json = new Object();
+var chat_ping = null;
+
+if(document.layers)
+    document.captureEvents(Event.KEYDOWN);
+       document.onkeydown =
+    function (evt) {
+        var keyCode = evt ? (evt.which ? evt.which : evt.keyCode) : event.keyCode;
+        if (keyCode == 13) { 
+                       document.getElementById('send').click();
+        } else return true;
+    };
+
+function parse_message_response(){
+       var message_count = chat_json.m.length;
+       if(message_count > 0) { beep_sound.play(); }
+       var message_text = "";
+       for(var i = 0; i < message_count; i++){
+               message_text += '<div><font color="red">' + chat_json.m[i].t + '</font>&nbsp;';
+               message_text += '<font color="blue">' + chat_json.m[i].n + '</font>&nbsp;';
+               message_text += chat_json.m[i].c + '</div>';
+               last_chat_id = chat_json.m[i].id;
+       }
+       document.getElementById("chat_window").innerHTML += message_text;
+}
+
+function update_ping(){
+       if(chat_ping > 2000){ document.getElementById("chat_status").innerHTML = "<img src='./gfx/bullet_red.png'>"; }
+       else if (chat_ping > 1000){ document.getElementById("chat_status").innerHTML = "<img src='./gfx/bullet_yellow.png'>"; }
+       else { document.getElementById("chat_status").innerHTML = "<img src='./gfx/bullet_green.png'>"; }
+}
+
+function getChat(url, pageElement){
+       // START TIMER
+       var timer = new Date();
+       var t_start = timer.getTime();
+       //debugEvent(url, "get");
+
+       // WE USE A JAVASCRIPT FEATURE HERE CALLED "INNER FUNCTIONS"
+       // USING THESE MEANS THE LOCAL VARIABLES RETAIN THEIR VALUES AFTER THE OUTER FUNCTION
+       // HAS RETURNED. THIS IS USEFUL FOR THREAD SAFETY, SO
+       // REASSIGNING THE ONREADYSTATECHANGE FUNCTION DOESN'T STOMP OVER EARLIER REQUESTS.
+       function ajaxBindCallback(){
+               if(ajaxRequest.readyState == 0) { window.status = "Waiting...";      }
+               if(ajaxRequest.readyState == 1) { window.status = "Loading Page..."; }
+               if(ajaxRequest.readyState == 2) { window.status = "Data Received...";}
+               if(ajaxRequest.readyState == 3) { window.status = "Interactive...";  }
+               if(ajaxRequest.readyState == 4) {
+                       window.status = "Transaction Complete...";
+
+      // STOP TIMER AND FIND DIFFERENCE
+      // MUST CREATE NEW TIMER INSTANCE :)
+      var timer2 = new Date();
+      var t_end = timer2.getTime();
+      var t_diff = t_end - t_start;
+      chat_ping = t_diff;
+      update_ping();
+
+      // TEST HTTP STATUS CODE - DISPLAY IN DUBUGGER AND STATUS
+      switch (ajaxRequest.status.toString()) {
+        case "200" :
+          window.status = "Page Loaded Sucessfully";
+                 chat_json = JSON.parse(ajaxRequest.response);
+                 parse_message_response();
+          debugEvent(url, "got", ajaxRequest.responseText, t_diff);
+          break;
+        case "403" :
+          window.status = "Forbidden...";
+          debugEvent(url, "error_403", ajaxRequest.responseText, t_diff);
+          break;
+        case "404" :
+          window.status = "File Not Found...";
+          debugEvent(url, "error_404", ajaxRequest.responseText, t_diff);
+          break;
+        case "500" :
+          window.status = "File Not Found...";
+          debugEvent(url, "error_500", ajaxRequest.responseText, t_diff);
+          break;
+        default :
+          window.status = "Unknown Ajax Error..."+ajaxRequest.status.toString();
+        }
+                       }
+       }
+       var ajaxRequest = null;
+       // BIND OUR CALLBACK THEN HIT THE SERVER...
+       if (window.XMLHttpRequest) {
+               ajaxRequest = new XMLHttpRequest();
+               ajaxRequest.onreadystatechange = ajaxBindCallback;
+               ajaxRequest.open("GET", url, true);
+               ajaxRequest.send(null);
+       } else if (window.ActiveXObject) {
+               ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
+               if (ajaxRequest) {
+                       ajaxRequest.onreadystatechange = ajaxBindCallback;
+                       ajaxRequest.open("GET", url, true);
+                       ajaxRequest.send();
+                       }
+               }
+}
+
+function start_updates(){
+       beep_sound = document.getElementById('beep_sound');
+       beep_sound.src = 'sfx/beep.wav';
+       click_sound = document.getElementById('click_sound');
+       click_sound.src = 'sfx/click.wav';
+       document.getElementById('message').focus();
+       chat_div = document.getElementById("chat_window");
+       getChat( "im_chat_update.php?id=-1", "chat_window");
+       setInterval(update_chat_messages, 2000);
+       update_users_online();
+       setInterval(update_users_online, 15000);
+}
+
+function update_chat_messages(){
+       getChat( "im_chat_update.php?id="+last_chat_id, "chat_window");
+       chat_window.scrollTop = chat_window.scrollHeight;
+}
+
+function update_users_online(){
+       getPage("im_user_update.php", "chat_users", "");
+}
+
+function send_message(){
+       sendData("im_send_message.php?message="+document.getElementById("message").value);
+       click_sound.play();
+       document.getElementById("message").value = '';
+}
+
+function disable_sound(){
+       beep_sound.src = '';
+       click_sound.src = '';
+}
+
+</script>
+
+<style>
+
+body {
+       font-family: Tahoma, sans-serif;
+       font-size: 14px;
+       background-color: #c5bcff;
+}
+#chat_window {
+       border: solid 1px #000;
+       background-color: #fff;
+       float: left;
+       width: 490px;
+       padding-left: 10px;
+       overflow:hidden;
+       height:300px;
+}
+#chat_users {
+       border: solid 1px #000;
+       background-color: #dedede;
+       float: right;
+       width: 150px;
+       padding-left: 10px;
+       overflow:hidden;
+       height:300px;
+}
+#users_list {
+       list-style-type: none;
+}
+#users_list li {
+       padding-top: 5px;
+}
+#send {
+       padding: 10px;
+}
+</style>
+<body  onLoad="start_updates();">
+<audio id="beep_sound" style="display: none;"></audio>
+<audio id="click_sound" style="display: none;"></audio>
+<div id=chat_window></div>
+<div id=chat_users></div>
+<div id=chat_input>
+       <table><tr>
+               <td><textarea id="message" cols=65 rows=1></textarea></td>
+               <td><button id="send" onCLick="send_message();">Send</button></td>
+               <td><div id=chat_status><img src="./gfx/bullet_green.png"></div></td>
+               <tr><td><button onCLick="openDebug();">Debug</button></td><td></td></tr>
+       </tr></table>
+</div>
+</body>
+</html>
diff --git a/im_chat_update.php b/im_chat_update.php
new file mode 100644 (file)
index 0000000..389cb12
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+include_once("auth.php");
+//sleep(1);
+$_GET["user"] = mysql_real_escape_string($_GET["user"]);
+if($_GET["id"] == -1) {
+               $sql = "select chat_id, time(chat_time) as short_chat_time, name, content from chat, users where users.user_id = chat.user_id and chat_time > NOW() - interval 5 minute";
+       } else {
+               $sql = "select chat_id, time(chat_time) as short_chat_time, name, content from chat, users where users.user_id = chat.user_id and chat_id > ".$_GET["id"]." limit 100";
+       }
+$result = mysql_query($sql);
+$chat_data = '{"m":[';
+if(mysql_num_rows($result)){
+       while($row = mysql_fetch_array($result)){
+               $chat_data .= '{"id": '.$row[chat_id].',"t":"'.$row[short_chat_time].'","n":"'.$row[name].'","c":"'.$row[content].'"},';
+       }
+       $chat_data = substr($chat_data, 0, -1);
+       $chat_data .= ']}';
+} else $chat_data .= "]}";
+echo $chat_data;
+?>
diff --git a/im_send_message.php b/im_send_message.php
new file mode 100644 (file)
index 0000000..52ea093
--- /dev/null
@@ -0,0 +1,8 @@
+<?php
+include_once("auth.php");
+if (!$_GET["message"]) { die("No Chat Message Sent"); }
+$_GET["message"] = mysql_real_escape_string($_GET["message"]);
+$sql = 'insert into chat values ("", '.$user_id.', "'.$_GET["message"].'", NOW())';
+$result = mysql_query($sql);
+echo $sql;
+?>
diff --git a/im_user_update.php b/im_user_update.php
new file mode 100644 (file)
index 0000000..226ba3b
--- /dev/null
@@ -0,0 +1,17 @@
+<?php
+
+include_once("conn.php");
+
+/* get list of users that are currently looged in */
+$sql = "select name from users where last_click > NOW()-300 order by name";
+
+$result = mysql_query($sql);
+$users_online = "";
+while($row = mysql_fetch_array($result))
+{
+       $users_online .= '<div>'.$row['name'].'</div>';
+}
+
+echo $users_online;
+
+?>
index efae3b8..a182c44 100644 (file)
@@ -39,7 +39,6 @@ if($comment_count == 0) {
        $comment_time = absHumanTiming($comment_max);
 }
 
-
 $sql = 'select count(*), max(timeposted) from filecom';
 
 $result = mysql_query($sql);
@@ -53,7 +52,6 @@ if($filecomm_count == 0) {
        $filecomm_time = absHumanTiming($filecomm_max);
 }
 
-
 $sql = 'select count(*), max(time_post) from files';
 
 $result = mysql_query($sql);
@@ -75,6 +73,21 @@ $result = mysql_query($sql);
 $row = mysql_fetch_row($result);
 $std_locked = $row[0];
 
+$sql = 'select max(chat_time) from chat';
+
+$result = mysql_query($sql);
+$row = mysql_fetch_row($result);
+$chat_max = $row[0];
+$chat_max_time = absHumanTiming($chat_max);
+
+$sql = 'select count(*) from chat';
+$result = mysql_query($sql);
+$row = mysql_fetch_row($result);
+$chat_count = $row[0];
+
+// number of chat messages by hour
+//$sql = 'select hour(chat_time) as chat_hour, count(*) from chat group by chat_hour order by chat_hour';
+
                        // ADIMINISTRATOR MENU
 ?>
 <div class="col2">
@@ -113,7 +126,8 @@ $std_locked = $row[0];
 <div class="col">
        <table class="gridtable">
        <tr><th>Stat</th><th>Value</th><th>Human Time</th><th>Last Updated</th></tr>
-       <tr><td>Files:</td><td><?php echo $file_count; ?></td><td><?php echo $file_time; ?></td><td><?php echo $file_time; ?></td></tr>
+       <tr><td>Chat Messages:</td><td><?php echo $chat_count ?></td><td><?php echo $chat_max_time ?></td><td><?php echo $chat_max ?></td></tr>
+       <tr><td>Files:</td><td><?php echo $file_count; ?></td><td><?php echo $file_time; ?></td><td><?php echo $file_max; ?></td></tr>
        <tr><td>File Comments:</td><td><?php echo $filecomm_count; ?></td><td><?php echo $filecomm_time; ?></td><td><?php echo $filecomm_max; ?></td></tr>
        <tr><td>Comments:</td><td><?php echo $comment_count; ?></td><td><?php echo $comment_time; ?></td><td><?php echo $comment_max; ?></td></tr>
        <tr><td>Assignments:</td><td><?php echo $assignment_count; ?></td><td><?php echo $assignment_time; ?></td><td><?php echo $assignment_max; ?></td></tr>
diff --git a/sfx/beep.wav b/sfx/beep.wav
new file mode 100644 (file)
index 0000000..c89a398
Binary files /dev/null and b/sfx/beep.wav differ
diff --git a/sfx/click.wav b/sfx/click.wav
new file mode 100644 (file)
index 0000000..a124ddd
Binary files /dev/null and b/sfx/click.wav differ
diff --git a/sfx/doorknock.wav b/sfx/doorknock.wav
new file mode 100644 (file)
index 0000000..502f903
Binary files /dev/null and b/sfx/doorknock.wav differ
index eccf28e..26c0559 100644 (file)
--- a/style.css
+++ b/style.css
@@ -62,32 +62,57 @@ table.gridtable td {
     border-color: #666666;
     background-color: #ffffff;
 }
-.menu {
-       /*background-color: #333;*/
+
+.login_menu {
+       position: relative;
+       float: right;
+       top: 0px;
+       right: 0px;
+       padding-right: 10px;
 }
 
-.menu ul {
+.logout_button {
+       background-color: #f00;
+       color: #fff;
+}
+
+.logout_button:hover {
+       background-color: #000;
+       color: #f00;
+}
+
+.main_menu {
+       position: absolute;
+       top: 0px;
+       left: 0px;
+       background-color: #ececec;
+       border-bottom: 2px solid #000;
+       width: 100%;
+       padding-top: 5px;
+       padding-bottom: 5px;
+}
+
+.main_menu ul {
        margin: 0;
        padding: 0;
-       float: left;
 }
 
-.menu ul li {
+.main_menu ul li {
        display: inline;
 }
 
-.menu ul li a {
+.main_menu ul li a {
        float: left; 
        text-decoration: none;
        margin-left: 25px;
 }
 
-.menu ul li a:visited {
+.main_menu ul li a:visited {
 
        text-decoration: none;
 }
 
-.menu ul li a:hover, .menu ul li .current {
+.main_menu ul li a:hover, .menu ul li .current {
     color: #000;
     border-bottom: 2px solid #000;
 }
@@ -120,12 +145,6 @@ table.gridtable td {
 }
 */
 
-
-.header {
-    position: absolute;
-    top: 5px;
-    right: 20px;
-}
 .highlight { background: #fff }
 .line_numbers {
     background-color: #ececec;