Tworzenie interaktywnych aplikacji internetowych'. Mój problem polega na fakcie, iż skrypt
z tej książki(ściągniety bezpośrednio z serwera wydawnictwa) dotyczący czatu AJAX, po
wrzuceniu na serwer nie działa. Co ciekawe w każdej przeglądarce wyświetla inny bład. Jako
początkującemu niesamowicie cięzko mi znaleźć przyczyny błedu. Jedynymi zmianami, które
poczyniłem to wstawienie poprawnych danych bazy mysql
config.php
<?php // definiuje dane połączenia z bazą danych define('DB_USER', 'user'); define('DB_PASSWORD', 'test'); define('DB_DATABASE', 'database'); ?>
error_handler.php
<?php // ustawia metodę obsługi błędu użytkownika na error_handler // funkcja obsługująca błędy function error_handler($errNo, $errStr, $errFile, $errLine) { // czyści wygenerowane wcześniej dane wyjściowe // komunikat o błędzie danych wyjściowych $error_message = 'ERRNO: ' . $errNo . chr(10) . 'TEKST: ' . $errStr . chr(10) . 'LOKALIZACJA: ' . $errFile . ', linia ' . $errLine; // zapobiega wykonywaniu innych skryptyów PHP exit; } ?>
chat.php
<?php // odwołanie do pliku z klasą Chat require_once("chat.class.php"); // jakie operacja maja być wykonane $mode = $_POST['mode']; // domyślnie ustawia ostatnie id na 0 $id = 0; // tworzy nową instancję klasy Chat $chat = new Chat(); // jeśli ma być wywołana metoda SendAndRetrieve if($mode == 'SendAndRetrieveNew') { // pobiera parametry wysłanej wiadomości z aplikacji $name = $_POST['name']; $message = $_POST['message']; $color = $_POST['color']; $id = $_POST['id']; // sprawdza, czy podane wartości są poprawne if ($name != '' && $message != '' && $color != '') { // wysyła wiadomość do bazy danych $chat->postMessage($name, $message, $color); } } // jeżeli ma być wywołana metoda DeleteAndRetrieve elseif($mode == 'DeleteAndRetrieveNew') { // usuwa istniejące wiadomości $chat->deleteMessages(); } // jeżeli ma być wywołana metoda Retrieve elseif($mode == 'RetrieveNew') { // poniera identyfikator ostatniej wiadomości pobranej przez klienta $id = $_POST['id']; } // czyści dane wyjściowe // nagłówki są wysyłane aby zapobiec zapisywaniu starych danych przez przeglądarki // pobiera nową wiadomośc z serwera ?>
chat.class.php
<?php // uruchamia plik konfiguracyjny require_once('config.php'); // uruchamia obsługę błędów require_once('error_handler.php'); // klasa zawierająca funkcjonalności strony serwera class Chat { // obsługuje bazę danych private $mMysqli; // konstruktor otwiera połączenie z bazą danych function __construct() { // łączy się z bazą $this->mMysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_DATABASE); } // destruktor zamyka połączenie z bazą public function __destruct() { $this->mMysqli->close(); } // kasuje zawartość tabeli zawierającej wiadomości public function deleteMessages() { // tworzy zapytanie SQL, które dodaje nową wiadomość do serwera $query = 'TRUNCATE TABLE chat'; // wykonuje zapytanie SQL $result = $this->mMysqli->query($query); } /* Metoda postMessages dodaje nową wiadomość do bazy - $name odpowiada nazwie użytkownika wysłanej wiadomości - $messsage to wysłana wiadomość - $color zawiera parametry koloru wybranego przez użytkownika */ public function postMessage($name, $message, $color) { // usuwa znaki specjalne przez dodaniem wiadomości do bazy $name = $this->mMysqli->real_escape_string($name); $message = $this->mMysqli->real_escape_string($message); $color = $this->mMysqli->real_escape_string($color); // tworzy zapytanie SQL, które dodaje nową wiadomość do serwera $query = 'INSERT INTO chat(posted_on, user_name, message, color) ' . 'VALUES (NOW(), "' . $name . '" , "' . $message . '","' . $color . '")'; // wykonuje zapytanie SQL $result = $this->mMysqli->query($query); } /* Metoda retrieveMessages pobiera wiadomości, które zostały wysłane na serwer - parametr $id jest wysyłany przez klienta i odpowiada identyfikatorowi ostatniej wiadomości pobranej przez niego. Wiadomości o późniejszych id będą pobrane z serwera i wysłane klientowi w formacie XML */ public function retrieveNewMessages($id=0) { // usuwa znaki specjalne przez dodaniem wiadomości do bazy $id = $this->mMysqli->real_escape_string($id); // tworzy zapytanie SQL, które pobiera wiadomość do serwera if($id>0) { // pobiera wiadomości świeższe niż te o danym $id $query = 'SELECT chat_id, user_name, message, color, ' . 'DATE_FORMAT(posted_on, "%Y-%m-%d %H:%i:%s") ' . 'AS posted_on ' . 'FROM chat WHERE chat_id > ' . $id . ' ORDER BY chat_id ASC'; } else { // przy pierwszym uruchomieniu wyświetla ostatnich 50 wiadomości z serwera $query = 'SELECT chat_id, user_name, message, color, posted_on FROM ' . '(SELECT chat_id, user_name, message, color, ' . 'DATE_FORMAT(posted_on, "%Y-%m-%d %H:%i:%s") AS posted_on ' . 'FROM chat ' . 'ORDER BY chat_id DESC ' . 'LIMIT 50) AS Last50 ' . 'ORDER BY chat_id ASC'; } // wykonuje zapytanie $result = $this->mMysqli->query($query); // tworzy odpowiedź XML $response = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'; $response .= '<response>'; // wysyła czystą flagę $response .= $this->isDatabaseCleared($id); // sprawdza czy jest odpowiedź if($result->num_rows) { // przegląda w pętli wszystkie przechwycone wiadomości, aby stworzyć wiadomość wynikową while ($row = $result->fetch_array(MYSQLI_ASSOC)) { $id = $row['chat_id']; $response .= '<id>' . $id . '</id>' . '<color><![CDATA[' . $color . ']]></color>' . '<time>' . $time . '</time>' . '<name><![CDATA[' . $userName . ']]></name>' . '<message><![CDATA[' . $message . ']]></message>'; } // zamyka połączenie z bazą, jak szybko jest to możliwe $result->close(); } // kończy odpowiedź XML i wysyła ją $response = $response . '</response>'; return $response; } /* Metoda isDatabaseCleared sprawdza, czy baza została wyczyszczona od ostatniego wywołania serwera - parametr $id zawiera identyfikator ostatniej wiadomości pobranej rpzez klienta */ private function isDatabaseCleared($id) { if($id>0) { //sprawdzenie liczby wierszy o id mniejszym niż uzyskany od klienta mówi nam, czy baza była ostatnio czyszczona $check_clear = 'SELECT count(*) old FROM chat where chat_id<=' . $id; $result = $this->mMysqli->query($check_clear); $row = $result->fetch_array(MYSQLI_ASSOC); // jeśli baza była czyszczona, tablica musi być przeładowana if($row['old']==0) return '<clear>true</clear>'; } return '<clear>false</clear>'; } } ?>
get_color.php
<?php // nazwa pliku z obrazkiem $imgfile='palette.png'; // otwiera plik z obrazkiem na stronie $img=imagecreatefrompng($imgfile); // pobiera współrzędne punktu klikniętego przez użytkownika $offsetx=$_GET['offsetx']; $offsety=$_GET['offsety']; // pobiera kliknięty kolor $rgb = ImageColorAt($img, $offsetx, $offsety); $r = ($rgb >> 16) & 0xFF; $g = ($rgb >> 8) & 0xFF; $b = $rgb & 0xFF; // zwraca kod koloru ?>
chat.css
body { font-family: Tahoma, Helvetica, sans-serif; margin: 1px; font-size: 12px; text-align: left } #content { border: DarkGreen 1px solid; margin-bottom: 10px } input { border: #999 1px solid; font-size: 10px } #scroll { position: relative; width: 340px; height: 270px; overflow: auto } .item { margin-bottom: 6px } #colorpicker { text-align:center }
index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="pl" lang="pl"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link href="chat.css" rel="stylesheet" type="text/css" /> </head> <body onload="init();"> <noscript> Twoja przeglądarka nie obsługuje JavaScript!! </noscript> <table id="content"> <tr> <td> <div id="scroll"> </div> </td> <td id="colorpicker"> <img src="palette.png" id="palette" alt="Color Palette" border="1" onclick="getColor(event);"/> <br /> <input id="color" type="hidden" readonly="true" value="#000000" /> <span id="sampleText"> (tekst będzie wyglądać tak) </span> </td> </tr> </table> <div> <input type="text" id="userName" maxlength="10" size="10" onblur="checkUsername();"/> <input type="text" id="messageBox" maxlength="2000" size="50" onkeydown="handleKey(event)"/> <input type="button" value="Wyślij" onclick="sendMessage();" /> <input type="button" value="Skasuj wszystko" onclick="deleteMessages();" /> </div> </body> </html>
chat.js part I
/* chatURL - URL do aktualizacji wiadomości */ var chatURL = "chat.php"; /* getColorURL - URL do pobrania wybranego koloru RGB */ var getColorURL = "get_color.php"; /* tworzy obiekty XMLHttpRequest, które zaktualizują wiadomości rozmowy i pobiorą wybrany kolor*/ var xmlHttpGetMessages = createXmlHttpRequestObject(); var xmlHttpGetColor = createXmlHttpRequestObject(); /* zmienna, która określa jak często łączyć się z serwerem*/ var updateInterval = 1000; // w milisekundach // ustawione na true, wyświetla szczegółowy komunikat o błędach var debugMode = true; /* rozpoczyna zapisywanie wiadomości w pamięci podręcznej */ /* lastMessageID - tidentyfikator najświeższej wiadomości rozmowy */ var lastMessageID = -1; /* mouseX, mouseY - współrzędne kursora myszy */ var mouseX,mouseY; /* tworzy instancję obiektu XMLHttpRequest */ function createXmlHttpRequestObject() { //przechowa referencję do obiektu XMLHttpRequest var xmlHttp; // powinno zadziałać dla przeglądarek nowszych niż IE6 try { // próbuje utworzyć obiekt XMLHttpRequest xmlHttp = new XMLHttpRequest(); } catch(e) { // zakładając że przeglądarką jest IE6 lub starsza "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"); // sprawdza każdy prog id, aż któryś zadziała for (var i=0; i<XmlHttpVersions.length && !xmlHttp; i++) { try { // próbuje utworzyć obiekt XMLHttpRequest xmlHttp = new ActiveXObject(XmlHttpVersions[i]); } catch (e) {} } } // zwraca utworzony obiekt lub wyświetla wiadomość o błędzie if (!xmlHttp) alert("Błąd podczas tworzenia obiektu XMLHttpRequest."); else return xmlHttp; } /* ta funkcja rozpoczyna rozmowę; jest wykonywana w chwili załadowania strony */ function init() { // pobiera odwołanie do okna tekstowego, w którym użytkownik wpisuje nową wiadomość var oMessageBox = document.getElementById("messageBox"); // zapobiega uruchomieniu funkcji autouzupełniania oMessageBox.setAttribute("autocomplete", "off"); // odwołanie do tekstu próbnego var oSampleText = document.getElementById("sampleText"); // ustawia kolor domyślny na czarny oSampleText.style.color = "black"; // zapewnia nadanie użytkownikowi nazwy (np. generowanej automatycznie) checkUsername(); // rozpoczyna aktualizację nowego okna requestNewMessages(); } // funkcja, która zapewnia, że użytkownik dostanie nazwę, choćby generowaną automatycznie function checkUsername() { // zapewnia nadanie użytkownikowi nazwy (np. generowanej automatycznie) var oUser=document.getElementById("userName"); if(oUser.value == "") } /* funkcja wywołana po wciśnięciu przycisku "Wyślij"*/ function sendMessage() { // zachowuje wiadomość w zmiennych lokalnych i czyści okienko tekstowe var oCurrentMessage = document.getElementById("messageBox"); var currentUser = document.getElementById("userName").value; var currentColor = document.getElementById("color").value; // nie wysyła pustych wiadomości { // jeśli musimy wysłać i pobrać wiadomość params = "mode=SendAndRetrieveNew" + "&id=" + encodeURIComponent(lastMessageID) + "&color=" + encodeURIComponent(currentColor) + "&name=" + encodeURIComponent(currentUser) + "&message=" + encodeURIComponent(oCurrentMessage.value); // dodaje wiadomość do kolejki cache.push(params); // czyści okno tekstowe oCurrentMessage.value = ""; } } /* funkcja wywołana po wciśnięciu przycisku "Usuń wszystko" */ function deleteMessages() { // ustawia flagę, która określa, że wiadomości są usuwane params = "mode=DeleteAndRetrieveNew"; // dodaje wiadomość do kolejki cache.push(params); } /* wysyła asynchroniczne żądanie, aby pobrać nowe wiadomości, wysłać je lub usunąć*/ function requestNewMessages() { // pobiera nazwę użytkownika i kolor ze strony var currentUser = document.getElementById("userName").value; var currentColor = document.getElementById("color").value; // kontynuuje jeśli xmlHttpGetMessages nie jest pusty if(xmlHttpGetMessages) { try { // nie zaczyna nowej operacji na serwerze, jeśli jakaś jest wykonywana if (xmlHttpGetMessages.readyState == 4 || xmlHttpGetMessages.readyState == 0) { // przechowa parametry potrzebne do wykonania żądania na serwerze var params = ""; // jeśli w kolejce czekają wiadomości, to pobiera najstarszą if (cache.length>0) params = cache.shift(); // jeśli pamięć jest pusta, pobiera nową wiadomość else params = "mode=RetrieveNew" + "&id=" +lastMessageID; // wywołuje działania po stronie serwera xmlHttpGetMessages.open("POST", chatURL, true); xmlHttpGetMessages.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xmlHttpGetMessages.onreadystatechange = handleReceivingMessages; xmlHttpGetMessages.send(params); } else { // sprawdza ponownie czy są nowe wiadomości setTimeout("requestNewMessages();", updateInterval); } } catch(e) { displayError(e.toString()); } } } /* funkcja obsługująca odpowiedź HTTP podczas aktualizacji wiadomości*/ function handleReceivingMessages() { // kontynuuje jeśli proces jest zakończony if (xmlHttpGetMessages.readyState == 4) { // kontynuuje, jeśli status HTTP ma wartość "OK" if (xmlHttpGetMessages.status == 200) { try { // przetwarza odpowiedź serwera readMessages(); } catch(e) { // wyświetla wiadomość o błędzie displayError(e.toString()); } } else { // wyświetla wiadomość o błędzie displayError(xmlHttpGetMessages.statusText); } } }
chat.js part II
/* funkcja obsługująca odpowiedź serwera podczas aktualizacji wiadomości*/ function readMessages() { // pobiera odpowiedź serwera var response = xmlHttpGetMessages.responseText; // błąd serwera? if (response.indexOf("ERRNO") >= 0 || response.indexOf("błąd:") >= 0 || response.length == 0) throw(response.length == 0 ? "Pusta odpowiedź serwera." : response); // pobiera element dokumentu response = xmlHttpGetMessages.responseXML.documentElement; // pobiera flagę określającą czy okno rozmowy zostało wyczyszczone czy nie clearChat = response.getElementsByTagName("clear").item(0).firstChild.data; // jeśli flaga ma wartość true, trzeba wyczyścić okno wiadomości if(clearChat == "true") { // czyści okno wiadomości i zeruje id document.getElementById("scroll").innerHTML = ""; lastMessageID = -1; } // pobiera tablice z odpowiedzi serwera idArray = response.getElementsByTagName("id"); colorArray = response.getElementsByTagName("color"); nameArray = response.getElementsByTagName("name"); timeArray = response.getElementsByTagName("time"); messageArray = response.getElementsByTagName("message"); // dodaje nową wiadomość do okna rozmowy displayMessages(idArray, colorArray, nameArray, timeArray, messageArray); // przechowuje lokalnie ID ostatniej otrzymanej wiadomości if(idArray.length>0) lastMessageID = idArray.item(idArray.length - 1).firstChild.data; // ponownie uruchamia sekwencję setTimeout("requestNewMessages();", updateInterval); } /* funkcja dodająca nowe wiadomości do okna rozmowy */ function displayMessages(idArray, colorArray, nameArray, timeArray, messageArray) { // każdy przebieg pętli dodaje nową wiadomość for(var i=0; i<idArray.length; i++) { // pobiera szczegóły wiadomości var color = colorArray.item(i).firstChild.data.toString(); var name = nameArray.item(i).firstChild.data.toString(); var message = messageArray.item(i).firstChild.data.toString(); // tworzy kod HTML, który wyświetli wiadomość var htmlMessage = ""; htmlMessage += "<div class=\"item\" style=\"color:" + color + "\">"; htmlMessage += message.toString(); htmlMessage += "</div>"; // wyświetla wiadomość displayMessage (htmlMessage); } } // wyświetla wiadomość function displayMessage(message) { // pobiera obiekt scroll var oScroll = document.getElementById("scroll"); // sprawdza, czy pasek jest przewinięty var scrollDown = (oScroll.scrollHeight - oScroll.scrollTop <= oScroll.offsetHeight ); // wyświetla wiadomość oScroll.innerHTML += message; // przewija na dół pasek przewijania oScroll.scrollTop = scrollDown ? oScroll.scrollHeight : oScroll.scrollTop; } // funkcja wyświetlająca komunikat o błędzie function displayError(message) { // wyświetla wiadomość o błędzie ze szczegółami technicznymi, jeśli debugMode ma ustawienie true displayMessage("Błąd podczas dostępu do serwera! "+ (debugMode ? "<br/>" + message : "")); } /*obsługuje zdarzenie keydown aby określić, kiedy wciśnięto Enter*/ function handleKey(e) { // pobiera zdarzenie e = (!e) ? window.event : e; // pobiera kod wciśniętego znaku code = (e.charCode) ? e.charCode : ((e.keyCode) ? e.keyCode : ((e.which) ? e.which : 0)); // obsługuje zdarzenie keydown if (e.type == "keydown") { // jeśli został wciśnięty Enter (kod 13) if(code == 13) { // wysyła bieżącą wiadomość sendMessage(); } } } /* usuwa spacje z początku i końca łańcucha*/ { return s.replace(/(^\s+)|(\s+$)/g, "") } /* funkcja, która oblicza współrzędne kursora na stronie*/ function getMouseXY(e) { // specyficzne dla przeglądarki if(window.ActiveXObject) { mouseX = window.event.x + document.body.scrollLeft; mouseY = window.event.y + document.body.scrollTop; } else { mouseX = e.pageX; mouseY = e.pageY; } } /* wywołanie serwera do pobrania koloru RGB*/ function getColor(e) { getMouseXY(e); // nic nie robi, jeśli obiekt XMLHttpRequest ma wartość null if(xmlHttpGetColor) { // rozpoczyna ustalanie względnej pozycji myszy var offsetX = mouseX; var offsetY = mouseY; // pobiera odwołania var oPalette = document.getElementById("palette"); var oTd = document.getElementById("colorpicker"); // oblicza względną pozycję kursora w oknie if(window.ActiveXObject) { offsetX = window.event.offsetX; offsetY = window.event.offsetY; } else { offsetX -= oPalette.offsetLeft + oTd.offsetLeft; offsetY -= oPalette.offsetTop + oTd.offsetTop; } // wywołuje serwer asynchronicznie, aby pobrać wybrany kolor try { if (xmlHttpGetColor.readyState == 4 || xmlHttpGetColor.readyState == 0) { params = "?offsetx=" + offsetX + "&offsety=" + offsetY; xmlHttpGetColor.open("GET", getColorURL+params, true); xmlHttpGetColor.onreadystatechange = handleGettingColor; xmlHttpGetColor.send(null); } } catch(e) { // wyświetla komunikat o błędzie displayError(xmlHttp.statusText); } } } /* funkcja obsługująca odpowiedź HTTP */ function handleGettingColor() { // jeśli proces zostanie zakończony, decyduje co zrobić ze zwróconymi danymi if (xmlHttpGetColor.readyState == 4) { // tylko jeśli status HTTP jest "OK" if (xmlHttpGetColor.status == 200) { try { //zmienia kolor changeColor(); } catch(e) { // wyświetla komunikat o błędzie displayError(xmlHttpGetColor.statusText); } } else { // wyświetla komunikat o błędzie displayError(xmlHttpGetColor.statusText); } } } /* funkcja, która zmienia kolor wyświetlanej wiadomości*/ function changeColor() { response=xmlHttpGetColor.responseText; // błąd serwera? if (response.indexOf("ERRNO") >= 0 || response.indexOf("error:") >= 0 || response.length == 0) throw(response.length == 0 ? "Nie mogę zmienić koloru!" : response); // zmienia kolor var oColor = document.getElementById("color"); var oSampleText = document.getElementById("sampleText"); oColor.value = response; oSampleText.style.color = response; }