Jump to content

AJAX 'GET' with Vista Gadget


clancy
 Share

Recommended Posts

I wasn't sure where to post this... but I guess it comes under Java.-----Hello!I was wondering if someone would be able to help me out on a bit of a situation I am facing with AJAX.I have been attempting to code and produce a Windows Vista Gadget... which is a media player playing our (I work at a radio station) internet live stream.Everything worksIt's just the AJAX part of the gadget that is causing me trouble.We have a server, which allows us to upload HTML, XML and txt documents with information regarding the current song. I first decided to use AJAX to obtain the track information from the XML file... but, it was ripping the whole file (tags included). So, I decided that the text document would be the go, as it would only display the title and artist - after being parsed through our HitPlayer which inserts the information where: {PL1_TITLE?} and {PL1_ARTIST?} are present... this allowed me to say: {PL1_TITLE?} - {PL1_ARTIST?} in the text document... no other tags or information required.! My problem is...Currently, when you first install the gadget and press Play, it connects to the Stream Server, and Track Server (GET'ing the information for the track). This works. It displays the song information (yay). And has a scroll if needed.But. The track information will stay the same... it is not being updated...You uninstall the gadget, and press Play - it is still the same information. (You have to restart your computer to get new information)Below is the JS file... Just the AJAX bit though...I have commented where I think the problem is... trying to describe what I believe should happen.I also tried to say

if (output.equals(song)) {}

My decision was that Output and Song may be different objects? But, it came with parsing errors (This Object Does Not Exist)... so I assume that Equals doesn't exist in Vista (allowed)... Can someone please help!? It would be great to get this finished.I was wondering, as AjaxGet() is being called when the media Select Case is set to Play... does this mean that it is only going to the AjaxGet() function if you press play?I said, in the infoReceived() function song = "" to clear the song, so it will always != output and hence complete the IF statement. It was doing this (which meant that AjaxGet() was being called continually upon updateInterval being triggered)... however! It was still not obtaining new information... which leaves me wondering if the information is being Cached? Is this likely? And, if so, is there a way of clearing it in the infoRecieved() function to allow the IF statement to be properly parsed.Here is my code:

function AjaxGet(){	if (status) {		req = new ActiveXObject("Microsoft.XMLHTTP");		if (req) {			req.onreadystatechange = infoReceived;			req.open("GET", trackURL, true);			req.send(null);		}		setTimeout("AjaxGet()", updateInterval); // Where updateInterval is 5 seconds (5000ms)	}}function startScroll(){	var s = "	   " + song;	if (s.length > 28) {		pos += 0.25;		if (pos >= s.length + 15) {			pos = 0;		}		message.innerHTML = s.substring(pos, s.length);	} else {		message.innerHTML = s;	}	timer = setTimeout("startScroll()", 80);}function infoReceived(){	if (req.readyState == 4) { // AJAX has requested and receieved comfirmation from server... [4]		if (req.status == 200) { // Server Response... 200 (OK) proceed with track verification			output = req.responseText; // Make a tempary string to, and, store the server response (current track title and artist) for later verification			if (song != output) { // If the OUTPUT string does not match the current (locally) stored track information, then::				song = output; // Change the current (locally) stored track information to the tempary string (containing the latest, server string)				pos = 0; // Reset scroll position to 0				clearTimeout(timer); // Clear timer, to allow smooth - consistant scroll				startScroll(); // Call the startScroll function - to minipulate the received information and begin scroll			}		} else { // Server Response... not 200 (bad server, multiple choices, error) proceed with error response			song = "Track Server Error: " + req.status + ", Response: " + req.statusText; // Display song information with error and response		}	}}

Link to comment
Share on other sites

Interesting.Well, I'd advise you rethink your approach. What you're doing now is sending new requests and downloading data and verifying it every 5 seconds.Instead, try to set the server to change its last modification time, or better yet - set an Expires header with the remaining duration of the song as expiration time. On all requests, unconditionally fill in the data.The UA itself will cache the data, thus downloading it only when it's needed, and you'll be sure to always update the field when a download has been made.If you want to avoid a redraw, use "Last-Modified", and check the "status" of the request. If it's 200, update the info. If the Last-Modified time is the same, the status code would be 304.Note that besides solving the problem, this would also increase the performance of the application, giving your server, and your users, a better experience.

Link to comment
Share on other sites

Hmm... that sounds like a plan.I am just so baffled as to why my code isn't working. Can you see (apart from the laggy GET every 5 seconds) why this would not be working?In all reasoning, I went through the code - over and over... and came to the conclusion that all logic was right:When the AjaxGet function is called, check to see if the strings of Song and Output (where Output is the response string from the server) are not of equal value, if so, commit to parse the statement: change the string Song to Output, reset scroller position and so on... Why! Why! Why! isn't this working!?!?!?I'll try and figure out the Expires Header and stuff. I remember reading up on this on a tutorial site :)Thanks for your help.A friend said to have an alert(output) before the If (song != output) {} End If. I tried this, but I don't think the gadget allows certain commands - as it didn't work ! bummer.

Link to comment
Share on other sites

Try to remove the

if (song != output)

condition and directly fill in the data. When you set up caches on the server, you'll have to do this anyway. Doing so right now *might* solve the problem, but if it does, it will also drastically decrease performance until you set up the cache.

Link to comment
Share on other sites

  • 2 weeks later...

Ok!Made a bit of progress!!!The problem was that the text file didn't have a self-refresh function... therefore not being refreshed on the server...So... now I have included a html file with a refresh function allowing it to refresh the content of the page.And. Lo and behold! The string is now updated live. But! It was pulling all the tags from the html file... so I looked up on the internet and found the

xyz.replace(/<\/?[^>]+(>|$)/g, "");

This allowed me to remove all the content within the tags of the html file... yay!But! Hmm. Another problem occurred. For the html file (we can only have one, as or player uses the html file to input the information... so we can't have say: update1.html and update2.html because they both wont be updated), we need to have content to be displayed within the Head specifically for our SMS service... and then within the Body, we have the normal page text. We also have a Title.My problem is, now I am ripping the html file and removing the tags, the string made by the rip is including the Title, SMS Information and the Page Text. This makes the string: KIK FM, Darwin NT Fly Away (Cosmic Gate Remix) - Vincent de Moor Away (Cosmic Gate Remix) - Vincent de MoorI tried using:

var output = new String(req.getElementById("information")[0].text);// where req is the XMLHttpRequest()// where output will then be measured with the current song to see the difference

But I get errors upon the Ajax function being triggered:

A Runtime Error has occurred.Do you wish to debug?Line: 68Error: Object doesn't support this property or method

Is this because I am using an html file?Also, the getElementById("information") is aimed at:

<pre>	 <code id="information">		  {PL1_SONG?} - {PL1_ARTIST?}	 </code></pre>

Hrmmmms.

Link to comment
Share on other sites

You can't getElementById() from a non-xml object (which the XMLHttpRequest isn't). You could use another regular expression to grab just the body text

var output = req.responseText.match(/<body>(.*?)<\/body>/g);

Link to comment
Share on other sites

Thanks!I tried it out, but it is creating a string with : "Null"So, I'm assuming that's that suppose to happen!?Here is our HTML which is updated on the server with the track information:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head>	<title>KIK FM, Darwin NT	</title>	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">	<meta http-equiv="Refresh" content="15">	<comment><!--SMSTKSTART-->{PL1_SONG?} - {PL1_ARTIST?}<!--SMSTKEND--></comment></head>	<body>	<div id="information" align="center">		<tt><b>	 {PL1_SONG?} - {PL1_ARTIST?}</b></tt>	</div></body></html>

I used spaces so the scroller text will freeze... I couldn't use   because it was appearing with the string.. duh! Java isn't HTML! lol.

Link to comment
Share on other sites

Hm... try this

var output = req.responseText.match(/<body>(.*?)<\/body>/)[1];

Oh - where are you putting this?

Link to comment
Share on other sites

The function called when the Ajax result has returned...// NB.. I forgot the ; closer after output = ... ... I'm used to coding in basic! lol

function infoReceived(){	if (req.readyState == 4) {		if (req.status == 200) {			//var output = new String(req.getElementById("information")[0].text);			var output = req.responseText.match(/<body>(.*?)<\/body>/g);			if (song != output) {				song = output; // NB.. I forgot the; closer... I'm used to coding in basic! lol				pos = 0;				clearTimeout(timer);				startScroll();			}		} else {			song = "Track Server Error: " + req.status + ", Response: " + req.statusText;		}	}}

Saved it, and is still saying : "Null"... although sometimes it says: (this happened before I used your solution) "Error 300, Multiple Choices.... argh!"Your code is looking to work. It's just that something is wrong with our server... I am however - uploading some music shows (will this affect the status?)EDIT:: With

output = req.responseText.match(/<body>(.*?)<\/body>/)[1]

It is giving errors:

"'req.responseText.match().1' is null or not an object"

Link to comment
Share on other sites

Ok, I think it is saying NULL because there is something wrong with our server. Our web page isn't displaying data (which is a good indication that something is wrong)... I'll have a chat with my boss to see what the matter is.Thanks!!! Hopefully this can fix the one thing I have been trying to fix for 3 months

:)

Ohk! It is still displaying NULL but the web page is now displaying the correct information... help please?

Link to comment
Share on other sites

Is it possible that you convert the HTML page to XHTML one instead, ideally also remove the DTD?The idea is to use responseXML instead of responseText if at all possible. But that would only work on output that is really XML. It's just the best way to serve structured data, that's all. It will also eliminate the need for all regular expressions. Getting the element would be as straightforward as saying something like:

responseXML.getElementById('information').nodeValue

Link to comment
Share on other sites

Well, that was my first approach... and looking at it now - I believe that the live update can only be achieved if there is a refresh function within the file... can refresh be done with XHTML?So, would it be a refresh (done with HTML), with say:

<information>{PL1_SONG?} - {PL1_ARTIST?}</information>

--of course make the file legal with XML and HMTL declared.If not... how could a XML file be coded to refresh ever 15 seconds on the server, with just one tag (it doesn't need chanel or others does it because it isn't RSS?) which will be filled by our HitPlayer with the track information?As you say - that will be the best option, both easy and structured correctly.Thanks!

Link to comment
Share on other sites

Well, that was my first approach... and looking at it now - I believe that the live update can only be achieved if there is a refresh function within the file... can refresh be done with XHTML?So, would it be a refresh (done with HTML), with say:
<information>{PL1_SONG?} - {PL1_ARTIST?}</information>

--of course make the file legal with XML and HMTL declared.If not... how could a XML file be coded to refresh ever 15 seconds on the server, with just one tag (it doesn't need chanel or others does it because it isn't RSS?) which will be filled by our HitPlayer with the track information?As you say - that will be the best option, both easy and structured correctly.Thanks!

I'm not sure you're seeing the idea here. The user doesn't "view" the page, so there's no need to restructure the page in any way. It works in the exact same way your text file works, but only with a different syntax being delievered to the application, so that fragments of data could be extracted with objects, instead of using regular expressions. So there's no way to code the XHTML to refresh, as that's simply out of the whole process. The idea is for the JavaScript to check at regular intervals if the X(HT)ML has been modified and by using the different DOM objects, to get the data in a specific element, which you can then use to update the value on the player.
Link to comment
Share on other sites

Hmmm. I see your point :)But... I am still confused as to why the:

var output = req.responseText.match(/<body>(.*?)<\/body>/g);

Is parsing 'NULL' as the output string. There is 'something' within the body! So why would it be saying there isn't? (NULL doesn't specifically refer to nothing though does it?)...I know there is something within the body, because when I use:

output.replace(/<\/?[^>]+(>|$)/g, "");

(or the easy way to check was the web page display... which, when View Source) the body contains information!Hmmm... any more help on this problem will be greatly appreciated! I think I'll add this forum to the Read Me Credits (so much help has come of this!!! lol)

Link to comment
Share on other sites

I think match() returns an array, whereas replace() returns a string. So if you want to get the full match as a string, you need to use

var output = req.responseText.match(/<body>(.*?)<\/body>/g)[0];

(arrays begin with "0" as a key, so it's no surprise that earlier, using "1" gave an error)I still think you should leave the regular expressions way though. It's hard, non efficient and unstable.Using XHTML instead is the opposite of all this. And I don't know about you, but I'm still concerned on the performance part. Users will hate this gadget if it's constantly sucking out their internet speed or even worse - if it's constantly sucking out of their CPU's power... and all just to keep up a string up to date. I for one would hate it. With Vista consuming a large portion of my CPU already and other programs already consuming large part of my internet connection (the radio station itself included), having one more thing to close is really a bummer.

Link to comment
Share on other sites

Ahh damn.The [0] didn't work either - same error response.Looks like I'll have to look at making the XHTML. I know where you are coming at from the performance and download side.It's just I really like looking at the gadget and going - aww wouldn't it be good if the scrolling text worked lol.Would I need to change much of my code to incorporate the XHTML update? Will XHTML be hard to incorporate (and / or code!!)?I have an Intel Core Duo T2450 @ 2.00GHz laptop, with 0.99GB RAM, running Vista Home Premium, and I installed the Multi-Metre gadget for the CPU / RAM... it was running around

(Idle) :: CPU 1: 07%CPU 2: 12%RAM:	70%(Gadget Running) ::CPU 1: 16%CPU 2: 24%RAM:	70%

That is with a song playing in WMP11, Notepad++ open, and MSN running (Idle). I got the Gadget Running with the peaks - when the gadget was installed and playing, as well as the 'idle' programs (I guessed this to be when it was obtaining the AJAX information).I have coded, within the Settings window a function with AJAX to compare the local gadget's version number (stored manually within the gadgets Setting.html) with a html page I have created on our server. The html page on our server only contains the most recent gadget version (I will update that number if a newer version is released and ready for download)... I only call the AJAX function once when the Settings window is opened, no more periodic updates... but it occurs again if the user opens the Settings window again. Is that an ok (performance wise) way of letting the user be informed of a new gadget update? This also allows the user to click one simple link within the window and I have build a function to create a direct link to the download (so they click, and install).Will it be ok if I ask heaps of questions regarding XHTML implementation for the track information? I'll really like to get this off the ground... once again >_<Thanks for all your help! You guys will certainly get a mention in the Credits!!! Congrads - a great forum :)

Link to comment
Share on other sites

For the gadget version update, I think you shouldn't send any content at all when there's no new version.When the gadget starts, you could make one HEAD request to the file and only check if the status is 304 (not modified). If so, it obviously means there's no new version. If it's modified, this means there may be a new version. Make a new GET request and check up the version number then. Do the same when the user explicitly asks for an update in the settings.There's a bigger performance penalty this way when there IS a new version, but unless you update the gadget twice a day or more, it will be better doing so, because there will be less data transmitted when there's nothing new.Implementing XHTML instead of text shouldn't be that difficult. And actually, if you implement your own XML, you might even futher increase performance. Consider delivering an XML file like:

<info><nowPlaying>{PL1_SONG?} - {PL1_ARTIST?}</nowPlaying><!--Other stuff you need to check up for--></info>

This is obviously shorter than

<html><head><title>Information</title></head><body><div id="nowPlaying">{PL1_SONG?} - {PL1_ARTIST?}</div><!--Other stuff you need to check up for--></body></html>

Getting the nowPlaying info from the XML above is a matter of replacing:

var output = req.responseText

with

var output = req.responseXML.getElementsByTagName('nowPlaying').nodeValue

and getting data of other elements is just as easy. With XHTML, it's the same, only instead of getElementsByTagName, you'd use getElementById.And as for increasing performance in either scenarios, I've already told you in my first post about setting up the server to deliver last-modified or better yet - expires headers, so that you only make request when needed and when you make request, update only when modified. But when the data is modified (accoding to the header), don't check up if the data is the same. Just update it.

Link to comment
Share on other sites

Ahh.Ok, I'll try and get a better way of getting the track information.I made it so the gadget only checks for version updates at the users descrition... the user must push a button to check for newer releases.Once I get the better performance code written for the track information, I will utalise it to be used with the version ajax... to better perform.I was wondering, since I have tried the var output = req.responseText.match(/<body>(.*?)<\/body>/g)[0]; to no avail, I thought their could be a possible Replace method (I've used several in other languages).However, the tutorials and such I read up on on the net were not exactly what I was looking for. The code relied on using the strings with arrays (of which I cannot use for the gadget) and also on preset checks (is there an A in the string, or BAG in the string?)... I cannot use this, as I will never know what will be pulled from the server!I was wondering if their was a way of removing duplicate string items... as the string from our server is:KIK FM, DARWIN NT SONG NAME, SONG TITLE SONG NAME, SONG TITLEIt repeats the SONG NAME, SONG TITLE bits (as you no doubt see).I have been talking with my boss, and he say's that the KIK FM, DARWIN NT is ok to stay... so I don't need to worry about removing that.

Link to comment
Share on other sites

Actually, now that I think of it, there's even one better way you can do the version update. Send a single GET request, but to the URL, add the version number. The server will respond with status "304" and no response body, or "200" and a response body containing whatever is needed to have the new version (new version number, URL, an archive containing things to install... whatever you can trasmit in a single response). Which of the two responses you get will depend on the version number being sent, not the browser's cache. This means the nice performance will stay there even when the user has cleaned their cache. It also means only a single request, regardless of whether there's a new version or not.For example, version 2.0.0 sends a request tohttp://your-radio-station.com/vistaGadget/...p?version=2.0.0and the raw response it would receive when there's no update would be something like:

HTTP/1.1 304 Not ModifiedDate: Mon, 31 Dec 2007 14:23:37 GMTServer: Apache/2.2.6 (Win32) PHP/5.2.4Cache-Control: private, must-revalidate, max-age=0

and when there is, it would be something like

HTTP/1.1 200 OKDate: Mon, 31 Dec 2007 14:23:37 GMTServer: Apache/2.2.6 (Win32) PHP/5.2.4Cache-Control: private, must-revalidate, max-age=0Content-Length: 57Content-Type: text/plain2.0.1http://your-radio-station.com/vistaGadget/download/

Here, you could add more data if the application would need such to update itself. If the user needs to be asked for confirmation, this response may contain a changelog, so that he could see the changes and decide if (s)he wants those fixes and additions, if not, well... I don't know exactly what gadgets may need. You decide. In addition, the request URL may also contain additional variables to specify. Those variables may indicate for example, if the user wants to experiment with experimental alpha and beta releases of the gadget. For example, a request like:http://your-radio-station.com/vistaGadget/...stability=alphaif adjusted in the PHP file, may make the server respond with:

HTTP/1.1 200 OKDate: Mon, 31 Dec 2007 14:23:37 GMTServer: Apache/2.2.6 (Win32) PHP/5.2.4Cache-Control: private, must-revalidate, max-age=0Content-Length: 64Content-Type: text/plain2.0.1ahttp://your-radio-station.com/vistaGadget/download/alpha/

even when there's no actual final version.As for the duplicate string occuring from the server. You should find a way to tweak the server if possible, so that it doesn't send this duplicate string on the first place. Not only it will save you the trouble of removing it with a regular expression, it will also, again, increase performance.(you know, I find it funny how in your case here, whatever calls for less JS code writing also calls for better performance)P.S. Get Fiddler if you haven't already.

Link to comment
Share on other sites

Ok nice. I'll experiment with that :)Maybe even have beta and alpha releases... but, I'm not really sure if that would be necessary, as it will be likely that only one small feature be added, improved, or the gadget become more stable, less bugs and have better performance, so I doubt there will be an alpha or beta release of the new content. But, still... sounds like a great idea. I could do that if there will be a drastic change :) for some reason :SWell, we can't really change anything with the track server file. We can only have one file which can be used to grab the information from our player, and we need that file to contain two methods of letting people see what is playing. If we could have multiple files, then we could split the SMS system and the Web Display into two different files, therefore eliminating the duplicate text... but, we have to have the SMS code to allow our SMS system to send responses to our listeners... it's annoying that the var output = req.responseText.match(/<body>(.*?)<\/body>/g)[0]; isn't working.Had a look at Fiddler, nice :mellow:I'll get it when I get home.Thanks.

Link to comment
Share on other sites

Ok nice. I'll experiment with that :)Maybe even have beta and alpha releases... but, I'm not really sure if that would be necessary, as it will be likely that only one small feature be added, improved, or the gadget become more stable, less bugs and have better performance, so I doubt there will be an alpha or beta release of the new content. But, still... sounds like a great idea. I could do that if there will be a drastic change :) for some reason :SWell, we can't really change anything with the track server file. We can only have one file which can be used to grab the information from our player, and we need that file to contain two methods of letting people see what is playing. If we could have multiple files, then we could split the SMS system and the Web Display into two different files, therefore eliminating the duplicate text... but, we have to have the SMS code to allow our SMS system to send responses to our listeners... it's annoying that the var output = req.responseText.match(/<body>(.*?)<\/body>/g)[0]; isn't working.Had a look at Fiddler, nice :mellow:I'll get it when I get home.Thanks.
Why not create a serparate server file that will be used exclusively for the gadget? The file will fetch and serve the gadget independantly of the other file that is used on the site.If the algorithm for fetching the information is too complex you may slightly edit the main file to not display the information when a certain variable is set.Then, include the main file in a newly created file, but before inclusion, set up that variable. After inclusion, instead of displaying the information, set up the caches and create the XML file if the file is to be served.I once did a similar thing where admin controls were present on an otherwise "normal" page. If a variable was set, I didn't showed the finished page, but instead added the admin controls on top of it, and then displayed it. A variable was only set once another file was accessed, and to access that file, you had to previously log in (i.e. the file that sets the variable and includes the amdin controls was just not publically readable in any way). In your case, it would be the same deal, with the only difference that the file is publically readable via the gadget.
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...