I am working on a game in Construct2 at the moment.
It is a HTML5 Javascript Engine.
I probably implement clay.io inside it.
My question however is about "Rooms"
Clay.io also helps with Rooms. However I am not sure If I will take that offer.
https://clay.io/docs/rooms
So when I want to limit the users per game to 10 for example. Would I then need to run two servers?
The socket.io server recives and returns data.. But would two games running with each 10 people not confuse the servers data? When person A on server A shoots some1, that this information could somehow end up on Person B on server B?
Or do the assigned ID's prevent this somehow?
Here is the Example Server that I want to upgrade for my needs:
var entities = [], count = 0;
var io = require("socket.io").listen(8099);
var INITIAL_X = 5;
var INITIAL_Y = 5;
var INITIAL_VEL_X = 0;
var INITIAL_VEL_Y = 0;
io.set('log level', 1);
io.sockets.on("connection", function (socket) {
var myNumber = count++;
//assign number
var mySelf = entities[myNumber] = [myNumber, INITIAL_X, INITIAL_Y, INITIAL_VEL_X, INITIAL_VEL_Y];
//Send the initial position and ID to connecting player
console.log(myNumber + ' sent: ' + 'I,' + mySelf[0] + ',' + mySelf[1] + ',' + mySelf[2]);
socket.send('I,' + mySelf[0] + ',' + mySelf[1] + ',' + mySelf[2]);
//Send to conencting client the current state of all the other players
for (var entity_idx = 0; entity_idx < entities.length; entity_idx++) {
//send initial update
if (entity_idx != myNumber) {
entity = entities[entity_idx];
if (typeof (entity) != "undefined" && entity != null) {
console.log(myNumber + ' sent: C for ' + entity_idx);
socket.send('C,' + entity[0] + ',' + entity[1] + ',' + entity[2]);
//send the client that just connected the position of all the other clients
}
}
}
//create new entity in all clients
socket.broadcast.emit("message",
'C,' + mySelf[0] + ',' + mySelf[1] + ',' + mySelf[2]);
socket.on("message", function (data) {
//if (myNumber == 0)
// console.log(myNumber + ' sent: ' +data);
var new_data = data.split(',');
if (new_data[0] == 'UM') {
mySelf[1] = new_data[1];
mySelf[2] = new_data[2];
mySelf[3] = new_data[3];
mySelf[4] = new_data[4];
//Update all the other clients about my update
socket.broadcast.emit("message",
'UM,' + mySelf[0] + ',' + mySelf[1] + ',' + mySelf[2] + ',' + mySelf[3] + ',' + mySelf[4]);
}
else if (new_data[0] == 'S') { // a s message
var shoot_info = [];
shoot_info[0] = new_data[1]; //ini x
shoot_info[1] = new_data[2]; //ini y
shoot_info[2] = new_data[3]; //degrees
//Update all the other clients about my update
socket.broadcast.emit("message",
'S,' + mySelf[0] + ',' + shoot_info[0] + ',' + shoot_info[1] + ',' + shoot_info[2]);
}
});
});
Socket.io has rooms that you can limit the broadcasts to, see: https://github.com/LearnBoost/socket.io/wiki/Rooms
Then rather than use socket.broadcast.emit you would use io.sockets.in('roomname').emit
A good way to mesh this with Clay.io is to have the room name be the room.id (in the Construct 2 plugin that's the RoomId expression). When the Clay.io room fills up (in C2 there's a condition for that), create the Socket.io room using that unique ID and put the players who "Rooms Filled" was just called for in that room.
I know it's a bit different since it's a game written in CoffeeScript instead of Construct 2, but we're using Clay.io rooms + Socket.io rooms in our Slime Volley game. Here is the code.
Related
I want to compare my list of stocks price with my set Stoploss which is stored and once the condition trigger alert by email. Below is my code
function emailAlert()
{
var stock1nameRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Watchlist").getRange("A5");
var stock1name = stock1nameRange.getValues();
var stock1cmpRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Watchlist").getRange("B5");
var stock1cmp = stock1cmpRange.getValues();
var stock1s1Range = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Watchlist").getRange("AK5");
var s1 = stock1s1Range.getValues();
var stock1s2Range = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Watchlist").getRange("AL5");
var s2 = stock1s2Range.getValues();
var ui = SpreadsheetApp.getUi();
if(stock1cmp<s1)
{
if(stock1cmp<s2)
{
ui.alert( stock1name + stock1cmp + 'is less than' +s2 );
var message = stock1name + stock1cmp + 'is less than' +s2 ;
MailApp.sendEmail("#gmail.com", "Stock Watchlist alert", message)
return s2
}
else
{
ui.alert( stock1name + stock1cmp + 'is less than' +s1 );
var message = stock1name + stock1cmp + 'is less than' +s1 ;
MailApp.sendEmail("#gmail.com", "Stock Watchlist alert", message )
return s1
}
}
}
This is for single stock. How can I make it more generic and compile all the stock list which pass the condition into single mail. Thanks.
Sen
In order to be able to select all the values you will be needing a for loop.
Snippet
function emailAlert() {
var ui = SpreadsheetApp.getUi();
var sheet = SpreadsheetApp.getActive().getSheetByName("Watchlist");
var stockName = sheet.getRange("A5:FINAL_RANGE").getValues();
var stockCmp = sheet.getRange("B5:FINAL_RANGE").getValues();
var s1 = sheet.getRange("AK5:FINAL_RANGE").getValues();
var s2 = sheet.getRange("AL5:FINAL_RANGE").getValues();
for (var i = 0; i < stockName.length; i++) {
if (stockCmp[i][0] < s1[i][0]) {
if (stockCmp[i][0] < s2[i][0]) {
ui.alert(stockName[i][0] + stockCmp[i][0] + ' is less than ' + s2[i][0]);
var message = stockName[i][0] + stockCmp[i][0] + ' is less than ' + s2[i][0];
MailApp.sendEmail("#gmail.com", "Stock Watchlist Alert", message);
return s2[i][0];
} else {
ui.alert(stockName[i][0] + stockCmp[i][0] + ' is less than ' + s1[i][0]);
var message = stockName[i][0] + stockCmp[i][0] + ' is less than ' + s1[i][0];
MailApp.sendEmail("#gmail.com", "Stock Watchlist Alert", message);
return s1[i][0];
}
}
}
}
Explanation
The above code loops through all the values from your columns by using a for loop and then based on the conditions you set, is sending the email and alerting the user. The range is retrieved by using the getRange() method with the a1Notation parameter. The a1Notation parameter here is represented by the start and the end of the range in which you have the values you need for the script.
Note
The above script is built considering the fact that the stockName, stockCmp, s1, s2 are all associated, meaning that they all have the same number of values stored in them.
Reference
Apps Script Range Class - getValues();
Apps Script Sheet Class - getRange(a1Notation);
JavaScript For Loop.
GAS is quite powerful and you could write a full fledged web-app using a Google Sheet as the DB back-end. There are many reasons not to do this but I figure in some cases it is okay.
I think the biggest issue will be performance issues when looking for rows based on some criteria in a sheet with a lot of rows. I know there are many ways to "query" a sheet but I can't find reliable information on which is the fastest.
One of the complexities is that many people can edit a sheet which means there are a variable number of situations you'd have to account for. For the sake of simplicity, I want to assume the sheet:
Is locked down so only one person can see it
The first column has the row number (=row())
The most basic query is finding a row where a specific column equals some value.
Which method would be the fastest?
I have a sheet with ~19k rows and ~38 columns, filled with all sorts of unsorted real-world data. That is almost 700k rows so I figured it would be a good sheet to time a few methods and see which is the fastest.
method 1: get sheet as a 2D array then go through each row
method 2: get sheet as a 2D array, sort it, then using a binary search algorithm to find the row
method 3: make a UrlFetch call to Google visualization query and don't provide last row
method 4: make a UrlFetch call to Google visualization query and provide last row
Here are the my query functions.
function method1(spreadsheetID, sheetName, columnIndex, query)
{
// get the sheet values excluding header,
var rowValues = SpreadsheetApp.openById(spreadsheetID).getSheetByName(sheetName).getSheetValues(2, 1, -1, -1);
// loop through each row
for(var i = 0, numRows = rowValues.length; i < numRows; ++i)
{
// return it if found
if(rowValues[i][columnIndex] == query) return rowValues[i]
}
return false;
}
function method2(spreadsheetID, sheetName, columnIndex, query)
{
// get the sheet values excluding header
var rowValues = SpreadsheetApp.openById(spreadsheetID).getSheetByName(sheetName).getSheetValues(2, 1, -1, -1);
// sort it
rowValues.sort(function(a, b){
if(a[columnIndex] < b[columnIndex]) return -1;
if(a[columnIndex] > b[columnIndex]) return 1;
return 0;
});
// search using binary search
var foundRow = matrixBinarySearch(rowValues, columnIndex, query, 0, rowValues.length - 1);
// return if found
if(foundRow != -1)
{
return rowValues[foundRow];
}
return false;
}
function method3(spreadsheetID, sheetName, queryColumnLetterStart, queryColumnLetterEnd, queryColumnLetterSearch, query)
{
// SQL like query
myQuery = "SELECT * WHERE " + queryColumnLetterSearch + " = '" + query + "'";
// the query URL
// don't provide last row in range selection
var qvizURL = 'https://docs.google.com/spreadsheets/d/' + spreadsheetID + '/gviz/tq?tqx=out:json&headers=1&sheet=' + sheetName + '&range=' + queryColumnLetterStart + ":" + queryColumnLetterEnd + '&tq=' + encodeURIComponent(myQuery);
// fetch the data
var ret = UrlFetchApp.fetch(qvizURL, {headers: {Authorization: 'Bearer ' + ScriptApp.getOAuthToken()}}).getContentText();
// remove some crap from the return string
return JSON.parse(ret.replace("/*O_o*/", "").replace("google.visualization.Query.setResponse(", "").slice(0, -2));
}
function method4(spreadsheetID, sheetName, queryColumnLetterStart, queryColumnLetterEnd, queryColumnLetterSearch, query)
{
// find the last row in the sheet
var lastRow = SpreadsheetApp.openById(spreadsheetID).getSheetByName(sheetName).getLastRow();
// SQL like query
myQuery = "SELECT * WHERE " + queryColumnLetterSearch + " = '" + query + "'";
// the query URL
var qvizURL = 'https://docs.google.com/spreadsheets/d/' + spreadsheetID + '/gviz/tq?tqx=out:json&headers=1&sheet=' + sheetName + '&range=' + queryColumnLetterStart + "1:" + queryColumnLetterEnd + lastRow + '&tq=' + encodeURIComponent(myQuery);
// fetch the data
var ret = UrlFetchApp.fetch(qvizURL, {headers: {Authorization: 'Bearer ' + ScriptApp.getOAuthToken()}}).getContentText();
// remove some crap from the return string
return JSON.parse(ret.replace("/*O_o*/", "").replace("google.visualization.Query.setResponse(", "").slice(0, -2));
}
My binary search algorithm:
function matrixBinarySearch(matrix, columnIndex, query, firstIndex, lastIndex)
{
// find the value using binary search
// https://www.w3resource.com/javascript-exercises/javascript-array-exercise-18.php
// first make sure the query string is valid
// if it is less than the smallest value
// or larger than the largest value
// it is not valid
if(query < matrix[firstIndex][columnIndex] || query > matrix[lastIndex][columnIndex]) return -1;
// if its the first row
if(query == matrix[firstIndex][columnIndex]) return firstIndex;
// if its the last row
if(query == matrix[lastIndex][columnIndex]) return lastIndex;
// now start doing binary search
var middleIndex = Math.floor((lastIndex + firstIndex)/2);
while(matrix[middleIndex][columnIndex] != query && firstIndex < lastIndex)
{
if(query < matrix[middleIndex][columnIndex])
{
lastIndex = middleIndex - 1;
}
else if(query > matrix[middleIndex][columnIndex])
{
firstIndex = middleIndex + 1;
}
middleIndex = Math.floor((lastIndex + firstIndex)/2);
}
return matrix[middleIndex][columnIndex] == query ? middleIndex : -1;
}
This is the function I used to test them all:
// each time this function is called it will try one method
// the first time it is called it will try method1
// then method2, then method3, then method4
// after it does method4 it will start back at method1
// we will use script properties to save which method is next
// we also want to use the same query string for each batch so we'll save that in script properties too
function testIt()
{
// get the sheet where we're staving run times
var runTimesSheet = SpreadsheetApp.openById("...").getSheetByName("times");
// we want to see true speed tests and don't want server side caching so we a copy of our data sheet
// make a copy of our data sheet and get its ID
var tempSheetID = SpreadsheetApp.openById("...").copy("temp sheet").getId();
// get script properties
var scriptProperties = PropertiesService.getScriptProperties();
// the counter
var searchCounter = Number(scriptProperties.getProperty("searchCounter"));
// index of search list we want to query for
var searchListIndex = Number(scriptProperties.getProperty("searchListIndex"));
// if we're at 0 then we need to get the index of the query string
if(searchCounter == 0)
{
searchListIndex = Math.floor(Math.random() * searchList.length);
scriptProperties.setProperty("searchListIndex", searchListIndex);
}
// query string
var query = searchList[searchListIndex];
// save relevant data
var timerRow = ["method" + (searchCounter + 1), searchListIndex, query, 0, "", "", "", ""];
// run the appropriate method
switch(searchCounter)
{
case 0:
// start time
var start = (new Date()).getTime();
// run the query
var ret = method1(tempSheetID, "Extract", 1, query);
// end time
timerRow[3] = ((new Date()).getTime() - start) / 1000;
// if we found the row save its values in the timer output so we can confirm it was found
if(ret)
{
timerRow[4] = ret[0];
timerRow[5] = ret[1];
timerRow[6] = ret[2];
timerRow[7] = ret[3];
}
break;
case 1:
var start = (new Date()).getTime();
var ret = method2(tempSheetID, "Extract", 1, query);
timerRow[3] = ((new Date()).getTime() - start) / 1000;
if(ret)
{
timerRow[4] = ret[0];
timerRow[5] = ret[1];
timerRow[6] = ret[2];
timerRow[7] = ret[3];
}
break;
case 2:
var start = (new Date()).getTime();
var ret = method3(tempSheetID, "Extract", "A", "AL", "B", query);
timerRow[3] = ((new Date()).getTime() - start) / 1000;
if(ret.table.rows.length)
{
timerRow[4] = ret.table.rows[0].c[0].v;
timerRow[5] = ret.table.rows[0].c[1].v;
timerRow[6] = ret.table.rows[0].c[2].v;
timerRow[7] = ret.table.rows[0].c[3].v;
}
break;
case 3:
var start = (new Date()).getTime();
var ret = method3(tempSheetID, "Extract", "A", "AL", "B", query);
timerRow[3] = ((new Date()).getTime() - start) / 1000;
if(ret.table.rows.length)
{
timerRow[4] = ret.table.rows[0].c[0].v;
timerRow[5] = ret.table.rows[0].c[1].v;
timerRow[6] = ret.table.rows[0].c[2].v;
timerRow[7] = ret.table.rows[0].c[3].v;
}
break;
}
// delete the temp file
DriveApp.getFileById(tempSheetID).setTrashed(true);
// save run times
runTimesSheet.appendRow(timerRow);
// start back at 0 if we're the end
if(++searchCounter == 4) searchCounter = 0;
// save the search counter
scriptProperties.setProperty("searchCounter", searchCounter);
}
I have a global variable searchList that is an array of various query strings -- some are in the sheet, some are not.
I ran testit on a trigger to run every minute. After 152 iterations I had 38 batches. Looking at the result, this is what I see for each method:
| Method | Minimum Seconds | Maximum Seconds | Average Seconds |
|---------|-----------------|-----------------|-----------------|
| method1 | 8.24 | 36.94 | 11.86 |
| method2 | 9.93 | 23.38 | 14.09 |
| method3 | 1.92 | 5.48 | 3.06 |
| method4 | 2.20 | 11.14 | 3.36 |
So it appears that, at least for my data-set, is using Google visualization query is the fastest.
I am trying to use Bing Custom Search's API for documents from Cognitive Services. The strange thing is that when I run it from India, it gives me more than a thousand results, but when I run it from a US server, it returns only 25 (sometimes 50 results). Here is the sample code for that:
var totalCount = 0;
var filetypes = new List<string> { "pdf", "docx", "doc" };
foreach (var filetype in filetypes)
{
var searchTerm = "microsoft%20.net%20resume+filetype%3a" + filetype;
Console.WriteLine("Searching for : " + filetype);
for (var i = 0; i < 40; i++)
{
var nextCount = 0;
var url = "https://api.cognitive.microsoft.com/bingcustomsearch/v7.0/search?" +
"q=" + searchTerm +
"&customconfig=" + customConfigId +
"&count=25" + "&offset=" + ((i * 25) + nextCount);
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", subscriptionKey);
var httpResponseMessage = client.GetAsync(url).Result;
var responseContent = httpResponseMessage.Content.ReadAsStringAsync().Result;
BingCustomSearchResponse response =
JsonConvert.DeserializeObject<BingCustomSearchResponse>(responseContent);
if (response.webPages == null || response.webPages.value.Length <= 0)
{
Console.WriteLine("response.webPages is null ");
break;
}
foreach (var webPage in response.webPages.value)
{
Console.WriteLine("name: " + webPage.name);
Console.WriteLine("url: " + webPage.url);
Console.WriteLine("displayUrl: " + webPage.displayUrl);
Console.WriteLine("snippet: " + webPage.snippet);
Console.WriteLine("dateLastCrawled: " + webPage.dateLastCrawled);
Console.WriteLine();
}
totalCount = totalCount + response.webPages.value.Length;
}
}
}
The subscription key I am using is a trial key.
I got the reason of this behavior. Actually it had nothing to do with region/country/market.
After looking into the response i got this message.
"Rate limit is exceeded. Try again in 1 seconds"
It means for after each call in the loop i have to wait for 1 second to give next call. Now need to know is this limit for trial subscription or this is kept for all calls to prevent DDOS attack or something.
May be from India it was working because may one iteraction is already taking one or more second.
Two things you can try: 1) In searchTerm, no need to use %20 and %3a, just use punctuations as you type in Bing, e.g. var searchTerm = "microsoft.net resume filetype::"+filetype, and 2) Enforce market by appending mkt=en-in (for India) or en-us (for US) in the query. You can do this by appending +"&mkt=en-in" at the end of url.
I presume for custom search you have selected domains (for both en-in and en-us markets) that return thousands of results for this query.
I've been using the YouMax plugin which enables you to embed your YouTube channel on your website. However, I am having problems as it displays the uploaded date in months and years. I'd like it to display days, weeks, months and years.
You can view the source code here http://jsfiddle.net/wCKKU/
I believe that its this that needs adjusting to make it calculate in day, weeks, months and years.
function getDateDiff(timestamp) {
if (null == timestamp || timestamp == "" || timestamp == "undefined") return "?";
var splitDate = ((timestamp.toString().split('T'))[0]).split('-');
var d1 = new Date();
var d1Y = d1.getFullYear();
var d2Y = parseInt(splitDate[0], 10);
var d1M = d1.getMonth();
var d2M = parseInt(splitDate[1], 10);
var diffInMonths = (d1M + 12 * d1Y) - (d2M + 12 * d2Y);
if (diffInMonths <= 1) return "1 month";
else if (diffInMonths < 12) return diffInMonths + " months";
var diffInYears = Math.floor(diffInMonths / 12);
if (diffInYears <= 1) return "1 year";
else if (diffInYears < 12) return diffInYears + " years"
}
You could modify the plugin by a small block of code in the middle of the function:
var d2M = parseInt(splitDate[1], 10); // this line is already there
var d1D = d1.getDate();
var d2D = parseInt(splitDate[2],10);
var diffInDays = (d1D + 30 *d1M + 12 * d1Y) - (d2D + 30 *d2M + 12 *d2Y);
if (diffInDays < 2) return "1 day";
else if (diffInDays < 7) return diffInDays+" days";
else if (diffInDays > 7 && diffInDays < 14) return "1 week";
else if (diffInDays > 14 && diffInDays < 30) return Math.floor(diffInDays / 7) + " weeks";
var diffInMonths = (d1M + 12 * d1Y) - (d2M + 12 * d2Y); // this line is already there
Note that this isn't a particularly elegant way to handle the issue, but it matches the coding style the plugin is already using, and at least won't break anything else.
Also, as a side comment, if you're modifying the plugin code you'll want to fix a bug in it at the same time. Getting the current month should look like this:
var d1M = d1.getMonth() + 1;
This is because in Javascript, the getMonth() function returns the month on a zero-based index, and your math won't be reliable unless you switch it to a one-based index.
I have a click function that does a jQuery/Ajax $.post to get data from a webservice when a span is clicked. When there is a Firebug break point set on the click function, everything works as expected (some new table tr's are appended to a table). When there is no break point set, nothing happens when you click the span. Firebug doesn't show any errors. I assume from other stackoverflow questions that this is a timing problem, but I don't know what to do about it. I have tried changing from a $.post to a $.ajax and setting async to false, but that didn't fix it. Here's the code for the click handler:
$('.rating_config').click(function(event){
event.preventDefault();
event.stopPropagation();
var that = $(this);
// calculate the name of the module based on the classes of the parent <tr>
var mytrclasses = $(this).parents('tr').attr('class');
var modulestart = mytrclasses.indexOf('module-');
var start = mytrclasses.indexOf('-', modulestart) + 1;
var stop = mytrclasses.indexOf(' ', start);
var mymodule = mytrclasses.substring(start, stop);
mymodule = mymodule.replace(/ /g, '+');
mymodule = mymodule.replace(/_/g, '+');
mymodule = encodeURI(mymodule);
// calculate the name of the property based on the classes of the parent <tr>
var propertystart = mytrclasses.indexOf('property-');
var propstart = mytrclasses.indexOf('-', propertystart) + 1;
var propstop = mytrclasses.indexOf(' ', propstart);
var myproperty = mytrclasses.substring(propstart, propstop);
myproperty = myproperty.replace(/ /g, '+');
myproperty = myproperty.replace(/_/g, '+');
myproperty = encodeURI(myproperty);
var parentspanid = $(this).attr('id');
// Remove the comparison rows if they are already present, otherwise generate them
if ($('.comparison_' + parentspanid).length != 0) {
$('.comparison_' + parentspanid).remove();
} else {
$.post('http://localhost/LearnPHP/webservice.php?user=user-0&q=comparison&level=property&module=' + mymodule + '&version_id=1.0&property=' + myproperty + '&format=xml', function(data) {
var data = $.xml2json(data);
for (var propnum in data.configuration.modules.module.properties.property) {
var prop = data.configuration.modules.module.properties.property[propnum];
console.log(JSON.stringify(prop));
prop.mod_or_config = 'config';
var item_id = mymodule + '?' + prop.property_name + '?' + prop.version_id + '?' + prop.value;
item_id = convertId(item_id);
prop.id = item_id;
//alert('prop.conformity = ' + prop.conformity);
// genRow(row, module, comparison, comparison_parentspanid)
var rowstring = genRow(prop, mymodule, true, parentspanid);
console.log('back from genRow. rowstring = ' + rowstring);
$(that).closest('tr').after(rowstring);
//$('tr#node-' + data[row].id + ' span#rating' + row.id).css('background', '-moz-linear-gradient(left, #ff0000 0%, #ff0000 ' + data[row].conformity + '%, #00ff00 ' + 100 - data[row].conformity + '%, #00ff00 100%');
var conformity_color = getConformityColor(prop.conformity);
$('tr#comparison_module_' + mymodule + '_setting_' + prop.id + ' span#module_' + mymodule + '_rating' + prop.id).css({'background':'-moz-linear-gradient(left, ' + conformity_color + ' 0%, ' + conformity_color + ' ' + prop.conformity + '%, #fffff0 ' + prop.conformity + '%, #fffff0 100%)'});
//$('tr#comparison-' + data[row].id + ' span#rating' + data[row].id).css('background','-webkit-linear-gradient(left, #00ff00 0%, #00ff00 ' + data[row].conformity + '%, #ff0000 ' + (100 - (data[row].conformity + 2)) + '%, #ff0000 100%)');
}
});
// Hide the Fix by mod column
hideFixedByModCol();
$('tr.comparison_' + parentspanid).each(function(i){
if (i % 2 == 0) {
$(that).addClass('comparison_even');
} else {
$(that).addClass('comparison_odd');
}
});
}
});
Any help would be greatly appreciated!
I suspect your data is coming back improperly formed. Enclose your code from the break on within try {} catch {} to see the error generated. Also it would be a good idea to add error processing to your ajax request.