PHP while loops causing an infinite load - javascript

I am trying to upload an image and some strings/numbers using AJAX. The code is working fine (as in, everything is getting into the database) except for one very important problem. I very much want the "character" variable to be unique, and every attempt I use to bring up an error message for trying a character name that has already been taken does not work. Here is my code:
AJAX:
function CSubmit() {
clearInterval(MoveDrawTimer);
var a=document.forms['form-id']["thefiles"].value; if (a == "") { document.getElementById("info").innerHTML="You must upload an image."; return };
if (showFileSize() > 5000000) { document.getElementById("info").innerHTML="File is too big. Maximum size is 5,000,000 bytes."; return };
var accepted = [".gif",".png",".jpg",".jpeg"];
if (Ext in oc(accepted) == true) {
var dataURLToBlob = function(dataURL) {
var BASE64_MARKER = ';base64,';
if (dataURL.indexOf(BASE64_MARKER) == -1) {
var parts = dataURL.split(',');
var contentType = parts[0].split(':')[1];
var raw = parts[1];
return new Blob([raw], {type: contentType});
}
var parts = dataURL.split(BASE64_MARKER);
var contentType = parts[0].split(':')[1];
var raw = window.atob(parts[1]);
var rawLength = raw.length;
var uInt8Array = new Uint8Array(rawLength);
for (var i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], {type: contentType}); };
newImage = dataURLToBlob(dataURL); } else { document.getElementById("info").innerHTML="Cannot send file. Check your extensions."; return };
var canvas = document.getElementById("Cinfo");
var context = canvas.getContext("2d");
context.clearRect(0, 0, 400, 400);
var TotalSpent = StrPts + IntPts + WisPts + SpdPts + MovPts;
var theform = document.getElementById("CharName");
if (theform.value == "") { document.getElementById("info").innerHTML="Your character must have a name."; return }
if ( /[^a-zA-Z0-9]/.test(theform.value) ) {
document.getElementById("info").innerHTML="Name must contain only letters and / or numbers."; return }
if (theform.value.length > 14) { document.getElementById("info").innerHTML="Character names must be 14 characters max."; return }
if (TotalSpent !== 2) { document.getElementById("info").innerHTML="You must spend exactly 2 points."; return }
var fd = new FormData();
fd.append('data', newImage);
fd.append('character', theform.value);
fd.append('str', StrPts);
fd.append('int', IntPts);
fd.append('wis', WisPts);
fd.append('spd', SpdPts);
fd.append('mov', MovPts);
//beginning of server transmission. above is javascript front end authentication.
//sending the following information:
//theform.value // character name
//StrPts, IntPts, WisPts, SpdPts, MovPts // the character stats
var xmlhttp;
if (window.XMLHttpRequest)
{ // code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{ // code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); }
xmlhttp.onreadystatechange=function(){ xmlhttp.addEventListener("progress", document.getElementById("info").innerHTML="Please wait . . ." , false);
if (xmlhttp.readyState==4 && xmlhttp.status==200) {
if (xmlhttp.responseText.length <= 14) {
document.getElementById("info").innerHTML="Congratulations, " + xmlhttp.responseText + " has been approved.";
SelectScreen();} } }
xmlhttp.open("POST","CHunique.php",true);
xmlhttp.send(fd);
};
As you can see, I am appending everything to a form and then sending it to CHunique.php, which is the following (currently an incomplete test page for demonstration of problem):
<?php
#$con=mysqli_connect("$db_host","$db_username","$db_pass", "$db_name") or die ("Could not connect to the database. Please try again later.");
mysqli_set_charset($con,"UTF8");
$Err = "fine";
session_start();
$login = $_SESSION['account'];
$CHresult = mysqli_query($con,"SELECT * FROM CHstats");
while($row = mysqli_fetch_array($CHresult)) {
$thecharacters = $row['character'];
if (strtolower($character) === strtolower($thecharacters)) { $Err = " Character name already in use."; break; }; }
The problem here is that I do not get any response back from the server at all. It stays infinitely on its "progress" function, which is to say "Please wait . . . " This is true for ALL while loops that I attempt on the php page...the server just gets stuck in loading. This is a huge problem because I need to use a while loop to loop through all the characters in order to see if the user's name is unique or not.
Again, everything is working fine, including the image upload, EXCEPT for this check. This check is very important, and I'm wondering why every single while loop I try in the php page just results in an infinite load time?? Please help.
Also keep in mind I do not use JQuery.

Why not just change your query to do the work for you, doesn't really make sense to do the comparison in PHP
$query = sprintf("SELECT * FROM CHstats WHERE LOWER(character) = LOWER('%s')", $thecharacters);
$result = mysqli_query($con, $query);
if(mysqli_num_rows($result) > 0){
// duplicate character name
}
Also, are you sure that the while loop is what's causing your page to hang? It is very likely that the connection to mysql isn't being made or is taking a long time. If you haven't already, move a die statement around your code to see where it is getting hung up.

YES! I actually solved it all on my own after several hours of frustration. The answer lies in my stupid AJAX conditional: if (xmlhttp.responseText.length <= 14). The responses were more than 14 characters and thus caused the server to load indefinitely.

Related

JavaScript EventSource won't open connection after Apache basic auth

I wasn't in charge of the Apache configuration, so I'm not sure what I can provide in terms of useful conf text, but I'm fairly certain I have narrowed the problem down to the login. EventSource works flawlessly both locally on XAMPP without any login and once you refresh the page after authenticating on the production server, but that first load on the server just will not open a connection. Has anyone seen this problem before? I couldn't find anything on the internet about this after searching for the past few days.
Edit: Some code
Some of the server-side code (which mostly shouldn't be relevant):
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
$client_stream = new RedisStream();
$client_stream->poll(1); //The loop, with sleep time as a parameter
The JavaScript:
var xhttpViewSet;
var xhttpSearch;
var view = 'tile';
var search = '';
var seed_url = '/core/seed_view.php';
var stream_url = '/core/stream.php';
var default_class = 'panel-default';
var success_class = 'panel-success';
var warning_class = 'panel-warning';
var danger_class = 'panel-danger';
function UpdateClient(c_name, c_obj) {
if ((c_element = document.getElementById(c_name)) !== null) {
c_element.classList.remove('text-muted');
c_element.classList.remove(default_class);
c_element.classList.remove(success_class);
c_element.classList.remove(warning_class);
c_element.classList.remove(danger_class);
switch (c_obj['status']) {
case 0:
c_obj['status'] = 'OK';
c_element.classList.add(success_class)
break;
case 1:
c_obj['status'] = 'Warning';
c_element.classList.add(warning_class)
break;
case 2:
c_obj['status'] = 'Critical';
c_element.classList.add(danger_class)
break;
default:
c_obj['status'] = 'Unknown';
c_element.classList.add(danger_class)
break;
}
for (i in c_obj) {
var var_nodes = c_element.getElementsByClassName(i);
if (var_nodes.length > 0) {
for (var j = var_nodes.length - 1; j >= 0; j--) {
var_nodes[j].innerHTML = c_obj[i];
}
}
}
}
}
function SetView() {
var view_url = seed_url + '?search=' + search + '&view=' + view;
xhttpViewSet.open('GET', view_url, true);
xhttpViewSet.send();
}
var main = function() {
container = document.getElementById('content');
if (new XMLHttpRequest()) {
xhttpViewSet = new XMLHttpRequest();
xhttpSearch = new XMLHttpRequest();
} else {
xhttpViewSet = new ActiveXObject('Microsoft.XMLHTTP');
xhttpSearch = new ActiveXObject('Microsoft.XMLHTTP');
}
var stream = new EventSource(stream_url);
stream.onopen = function() {
console.log('Connection opened.'); //This doesn't fire
}
stream.onmessage = function(e) {
var c_obj = JSON.parse(e.data);
UpdateClient(c_obj.name, c_obj.value);
};
xhttpViewSet.onreadystatechange = function() {
if (xhttpViewSet.readyState == 4) {
var resp = xhttpViewSet.responseText;
if (xhttpViewSet.status == 200 && resp.length > 0) {
container.innerHTML = resp;
if (view == 'list') {
$('#computer-table').DataTable({
"lengthMenu": [[25, 50, 100], [25, 50, 100]]
});
}
} else {
container.innerHTML = '<error>No computers matched your search or an error occured.</error>';
}
}
}
SetView(); //This successfully does all but make the EventSource connection, and only fails to do that on first load
document.getElementById('list-view').addEventListener('click', function() {
view = 'list';
SetView();
});
document.getElementById('tile-view').addEventListener('click', function() {
view = 'tile';
SetView();
});
document.getElementById('search').addEventListener('keyup', function() {
search = this.value.toUpperCase();
SetView();
});
document.getElementById('clear-search').addEventListener('click', function() {
document.getElementById('search').value = '';
search = '';
SetView();
});
};
window.onload = main;
It is a bit hard to know for sure without a lot more information, but based on what you have said so far, I think it is one of:
HEAD/OPTIONS: Some browsers will send a HEAD or OPTIONS http call to a server script, before they send the GET or POST. The purpose of sending OPTIONS is to ask what headers are allowed to be sent. It is possible this is happening as part of the login process; that might explain why it works when you reload. See chapter 9 of Data Push Apps with HTML5 SSE (disclaimer: my book) for more details; basically, at the top of your SSE script you need to check the value of $_SERVER["REQUEST_METHOD"] and if it is "OPTIONS", intercept and say what headers you want to accept. I've used this one before:
header("Access-Control-Allow-Headers: Last-Event-ID,".
" Origin, X-Requested-With, Content-Type, Accept,".
" Authorization");`
CORS: The HTML page URL and the SSE page URL must have identical origins. There are detailed explanations (specific to SSE) in chapter 9 of Data Push Apps with HTML5 SSE (again), or (less specifically) at Wikipedia. If this is the problem, look into adding header("Access-Control-Allow-Origin: *"); to your SSE script.
withCredentials: There is a second parameter to the SSE constructor, and you use it like this: var stream = new EventSource(stream_url, { withCredentials: true }); It is saying it is okay to send the auth credentials. (Again, chapter 9 of the book goes into more detail - sorry for the repeated plugs!) There is a second step, over on the server-side: at the top of your PHP SSE script you need to add the following.
header("Access-Control-Allow-Origin: ".#$_SERVER["HTTP_ORIGIN"]);
header("Access-Control-Allow-Credentials: true");
PHP Sessions locking: This normally causes the opposite problem, which is that the SSE script has locked the PHP session, so no other PHP scripts work. See https://stackoverflow.com/a/30878764/841830 for how to handle it. (It is a good idea to do this anyway, even if it isn't your problem.)

sending string with userid together with files through xmlhttprequest

I am using the following code to upload files without refreshing my page but I need to send the userid of the currently logged in user together with the files, which is stored in $_SESSION['UserID'].
var fileSelect = document.getElementById('file');
var files = fileSelect.files;
var formData = new FormData();
var div = document.getElementById('UploadInfo');
// Loop through each of the selected files.
for (var i = 0; i < files.length; i++) {
var file = files[i];
// Check the file type.
if (!file.type.match('image.*')) {
alert("File: " + file.name + " is not an image and will not be uploaded.");
continue;
}
// Add the file to the request.
formData.append('images[]', file, file.name);
}
var xhr = new XMLHttpRequest();
xhr.open('POST', 'Php/upload_file.php', true);
xhr.send(formData);
I have tried using the following line without success, which means i am not recieveing the userid value in my phpscript, $_POST is empty and $_FILES contains only the fileinfo.
formData.append('UserID', '<%=$_SESSION["UserID"] %>');
Is there any other way I can accomplish this?
My PHP code:
if(isset($_FILES)) {
$numberOfFiles = count($_FILES['images']['name']);
for($id = 0; $id < $numberOfFiles; $id++)
{
if (file_exists($_FILES["images"]["tmp_name"][$id])) {
$destination = "../UploadedImages/" . $_FILES['UserID'] . $_FILES["images"]["name"][$id];
move_uploaded_file($_FILES["images"]["tmp_name"][$id], $destination);
}
}
}
exit();
It looks like you need to use $_POST in your php script, you had $_FILES['UserID'] instead of $_POST['UserID'] .I also added a check to see if the UserID was passed, and added a variable $userId, then if $_POST['UserID'] did not get passed to the php script, i use die and send back an error.
NOTE: You should be checking $_POST['UserID'] to make sure it doesn't contain SQL injection scripts, or harmful code that can cause issues.
if(isset($_FILES) || isset($_POST)) {
$numberOfFiles = count($_FILES['images']['name']);
$userId = ''; //Create userId variable
//Check if the UserID exists in the autoglobal $_POST
if(array_key_exists('UserID', $_POST) === true) {
//Now we can add UserId to our variable
$userId = $_POST['UserID'];
} else {
die('UserID was not passed');
}
for($id = 0; $id < $numberOfFiles; $id++)
{
if (file_exists($_FILES["images"]["tmp_name"][$id])) {
$destination = $userId . $_FILES["images"]["name"][$id];
move_uploaded_file($_FILES["images"]["tmp_name"][$id], $destination);
}
}
} else {
echo "$_POST and $_FILES dont exist";
}
Edits: Fixed up some syntax errors, please re look at the code and make sure you have the latest version.

AJAX - JSON syntax error

I am working on a project for a client and they want to be able to update a list of costs dynamically depending on the registrants status as member/non-member or student. So I thought AJAX would have been the best way to do this but I am having trouble with my implementations. Every time I send my object I get a syntax error. I have placed the code below.
JavaScript
function checkMember(){
var member = document.getElementById("user_mem_id");
if(member.value == "" || member.value.trim() == ""){
document.getElementById("testError").innerHTML = "Please enter a membership id<br>";
document.getElementById("testError").style.color = "red";
}else{
var json = { "data":[{"membership_id":member.value}]}
var xmlHttp = false;
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");// For Old Microsoft Browsers
}catch (e) {
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");// For Microsoft IE 6.0+
}catch (e2) {
xmlHttp = false;// No Browser accepts the XMLHTTP Object then false
}
}
if (!xmlHttp && typeof XMLHttpRequest != 'undefined') {
xmlHttp = new XMLHttpRequest();//For Mozilla, Opera Browsers
}
xmlHttp.open("POST","member_check.php",true);
xmlHttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlHttp.send("data=" + json);
xmlHttp.onreadystatechange=function(){
if (xmlHttp.readyState==4 && xmlHttp.status==200){
document.getElementById("testError").innerHTML=xmlHttp.responseText;
console.log(xmlHttp.responseText);
json_last_error;
};
};
}
}
PHP
<?php
if(isset($_POST["data"]))
{
$data = $_POST["data"];
$res = json_decode($data, true);
echo $data[membership_id];
}
The other issue is that when I try and access the membership "cell" of the data array I get illegal off set string. Thus I thought I had declared my original JSON object incorrectly but I appears (as I have viewed many examples on here and else where) that I have declared that correctly.
I think you need to use stringify in order to perform the post successfully.
var json = { "data":[{"membership_id":member.value}]};
json_data = JSON.stringify(json);
then use json_data in your ajax call.
There are quite a few things wrong with your code. As I stated in my first comment to you, you need to escape your json before you send it in the query string, as json converted to a string, without any special rules applied turns into [object Object], and that isn't valid json, nor is it parsed as such.
To do that, use JSON.stringify(json);. Example:
function checkMember(){
var member = document.getElementById("user_mem_id");
if(member.value == "" || member.value.trim() == ""){
document.getElementById("testError").innerHTML = "Please enter a membership id<br>";
document.getElementById("testError").style.color = "red";
}else{
var json = { "data":[{"membership_id":member.value}]}
var xmlHttp = false;
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");// For Old Microsoft Browsers
}catch (e) {
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");// For Microsoft IE 6.0+
}catch (e2) {
xmlHttp = false;// No Browser accepts the XMLHTTP Object then false
}
}
if (!xmlHttp && typeof XMLHttpRequest != 'undefined') {
xmlHttp = new XMLHttpRequest();//For Mozilla, Opera Browsers
}
xmlHttp.open("POST","member_check.php",true);
xmlHttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlHttp.send("data=" + JSON.stringify(json));
//json turned into proper string
//I should also note, you should url-encode this string if it contains
//any special characters using encodeURIComponent()
xmlHttp.onreadystatechange=function(){
if (xmlHttp.readyState==4 && xmlHttp.status==200){
document.getElementById("testError").innerHTML=xmlHttp.responseText;
console.log(xmlHttp.responseText);
//json_last_error; //Commented this out, as it will
//echo an error, causing the script to halt, as it doesn't
//exist.
};
};
}
}
Secondly, you send an object whose key 'data' contains an array of objects.. not a simple object:
<?php
if(isset($_POST["data"]))
{
$data = $_POST["data"];
$res = json_decode($data, true);
echo $data[membership_id]; // There are many things wrong here
// ^ ^ this is not a constant and likely does not exist.
// |- This is still your string, which doesn't work
}
<?php
if(isset($_POST["data"]))
{
$data = $_POST["data"];
$res = json_decode($data, true);
echo $res['data'][0]['membership_id'];
// ^ ^ ^ ^
// | |first item |-string
//The result|-string
}
Hopefully my comments will be self explanatory.. but in case they are not... $res is your decoded array, 'data' is the first position of that array, 0 is the first position of the array at that position, and 'membership_id' is the item you want to access. You access members as strings, indexes as integers.
The basic problem with your code is the ";" terminator when you are defining variable. Check your following line
json = { "data":[{"membership_id":member.value}]}
You haven't put a semicolon at the end. (however it might still work a few times but mostly its an error)
Rather you have written a lot of code too. I would suggest you to use jquery's $.ajax function to simplify your task.
Also in case if you only have membership id in your json data its more easy to create a json object like the one below
var json = {"membership_id" : member.value " } ;
Also you need to send your json data after quoting in string using JSON.stringify(json)

ajax responsetext has extra data

I use ajax to check if there is a certain record in the database, using the variables 'zer' and 'modify' to search for the record containing those values (see the code below).
If the record is found in the database, I return "found" from the php ajax code to the javascript; otherwise I return "not found"
I also store the value of 'zer' and 'modify' into the SESSION before I return the "found" or "not found" from the php ajax code.
The response text looks like this when the record is found in the database:
array(2) {
["zer"]=>
string(2) "someVal"
["modify"]=>
string(1) "anotherVal"
}
found
I only want the "found" returned here and for some reason the responseText is returning the Session variables that I set up before the return.
Why is this, and how can I precisely control exactly what is returned from the Ajax call?
I tried using some buffer flushing php calls (see the code) to no effect. I've read a lot of Ajax posts but I cannot find one that explains how to have precise, exact control over what gets returned from my ajax, or whether storing my 'zer' and 'modify' values into the SESSION before I return the responseText is some kind of "no-no."
Here is the code (NOTE: the database lookup code works 100% and correctly finds the record if it is present -- the code below has been tested for both the 'found' and 'not found' situations and correctly locates the record if it is present).
EDIT: I use some output buffering PHP calls in an attempt to control any output buffering that may be happening, but this did not help. See these buffer calls below.
JAVASCRIPT
var zer = "someVal";
var modify = "anotherVal";
if(window.XMLHttpRequest)
{
xmlhttp = new XMLHttpRequest();
}
else
{
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
var theResponseText;
xmlhttp.onreadystatechange = function()
{
if(xmlhttp.readyState == 4 && xmlhttp.status == 200)
{
theResponseText = xmlhttp.responseText;
alert("responseText is >>>" + theResponseText + "<<< that.");
if( theResponseText == 'notfound')
{
return;
}
else if( theResponseText == 'found')
{
alert("We found the record!.")
}
}
}
ajaxText = "handler.php?zer=" + zer + "&modify=" + modify;
xmlhttp.open("GET", ajaxText, true);
xmlhttp.send();
PHP CODE in handler.php
$zer = $_GET['zer'];
$modify = $_GET['modify'];
$theDB = connectToDb();
$query = "SELECT * FROM " . Dbases::$MyDB_TABLE
. " WHERE BINARY " . dbConstants::$ZERS
. " IN ('" . $zer . "')"
. " AND BINARY " . dbConstants::$MODIFYS
. " IN ('" . $modify . "')";
$result = mysql_query($query);
if($result)
{
$numrows = mysql_num_rows($result);
if($numrows > 0)
{
$bFound = true;
}
}
closeDB($theDB);
// now erase the php output buffering, start a new output buffer,
// echo the result, then flush the php output buffer so that the
// Ajax response text has *exactly* what I 'echo' here and nothing else.
// NOTE: this did *not* help.
if(! $bFound)
{
ob_flush();
while (#ob_end_flush());
//ob_end_flush();
//ob_end_clean();
//ob_end_flush();
ob_start();
//echo "notfound";
print "notfound";
ob_end_flush();
//ob_end_clean();
// clear out the session vars
$_SESSION['zer'] = "";
$_SESSION['modify'] = "";
return;
}
else
{
ob_end_clean();
ob_start();
//echo "found";
print "found";
ob_end_flush();
//ob_end_clean();
$_SESSION['zer'] = $zer;
$_SESSION['modify'] = $modify;
return;
}
I solved this. One of the team members, at some point, stuck a var_dump way up in the source file and I didn't catch it until just now. I commented out the var_dump() and voila, the responseText is as expected, and I eliminated all the php buffer function calls as well.

Why wont this JS code work if its all on the same line?

I'm writing HTML code for a Java servlet. I first write the code in html/js so I can debug what I'm working on, and then I'll make it a Java string and put it in my servlet. My problem is that the code is working fine when I view it in Firefox from a local html file, but when I view it on my Java servlet, it doesn't work because the js isn't getting called.
What I did was format the html that my servlet generated so that its not all on a single line and ran the code again. This time it worked. I copied this working code into a browser address bar so that it will all be on a single line, and copied that code back into the script in my html file. Now, when the previously working code is on a single line, it doesn't work.
Here's the formatted JS:
var sMax
var holder;
var preSet;
var rated;
var request;
function rating(num){
sMax = 0;
for(n=0; n<num.parentNode.childNodes.length; n++){
if(num.parentNode.childNodes[n].nodeName == "A"){
sMax++;
}
}
if(!rated){
s = num.id.replace("_", '');
a = 0;
for(i=1; i<=sMax; i++){
if(i<=s){
document.getElementById("_"+i).className = "on";
document.getElementById("rateStatus").innerHTML = num.title;
holder = a+1;
a++;
}else{
document.getElementById("_"+i).className = "";
}
}
}
}
function off(me){
if(!rated){
if(!preSet){
for(i=1; i<=sMax; i++){
document.getElementById("_"+i).className = "";
document.getElementById("rateStatus").innerHTML = me.parentNode.title;
}
}else{
rating(preSet);
document.getElementById("rateStatus").innerHTML = document.getElementById("ratingSaved").innerHTML;
}
}
}
function rateIt(me){
if(!rated){
document.getElementById("rateStatus").innerHTML = document.getElementById("ratingSaved").innerHTML + " "+me.title;
preSet = me;
rated=1;
sendRate(me);
rating(me);
}
}
function sendRate(sel){
alert("Your rating was: "+sel.title);
addRating("rating", "?truck=kogibbq?rating="+ sel.id);
}
function addRating(servletName, servletArguments){
var servlet = servletName;
var arg = servletArguments
var req = servlet + arg;
alert(req);
addrequest(req);
request.onreadystatechange = function(){
alert("response received");
}
}
function addrequest(req) {
try {
request = new XMLHttpRequest();
}catch (e) {
try {
request = new ActiveXObject("Microsoft.XMLHTTP");
}catch (e) {
alert("XMLHttpRequest error: " + e);
}
}
request.open("GET", element, true);
request.send(null);
return request;
}
You are missing semi-colons on:
var sMax
and
var arg = servletArguments
Sarfraz has already pointed out the deal-breakers in this code, but for similar problems in the future, I recommend pasting your code into JSLint, for validation.
It'll find a lot of errors that won't actually break your code (but then neither will the lack of semicolons unless you put the entire script in one line), so you don't need to fix every single remark just to get it working, but of course, if you can follow the exact JSLint recommendations, that's usually just great.
There's a ; missing in the first line of your javascript file.

Categories