Quite possibly the most annoying part of typing up a lengthy post is accidentally clicking away from the page or closing the browser and losing all of your text. No more will that be an issue with a new feature being added to the NilsBlogPress suite (i really need to get a better name for when I am referring to this thing). Sadly that feature will NOT be autosave, let me explain..
The task sounded easy enough, create some sort of javascript function that would repeat every 30 seconds or so. This function would need to grab the current data inside the textarea used for writing post content, and some how save it to the database in a special autosave table. I immediately turned to AJAX (Asynchronous Javascript And Xml), as an asynchronous call to some other PHP script to do the work of backing up the data to the database would be low impact and let me do things behind the scenes without interrupting the post creation process.
Step one, dust off the AJAX tutorial code I had laying around after doing the W3Schools tutorials, and modify it to suit my needs. The tutorial was for a "hint-box" that would offer a suggestion as you typed a name into an input field:
This was the main triggering function, the input field on the page in question would trigger the function by onkeyup="showHint(document.getElem...)" passing in the text field data, and then send the data to "gethint.php" for processing. GetXmlHttpObject() is also called out in this sample to set up the AJAX calling variable (For source code for those 2 bolded bits mentioned above, check out the W3Schools AJAX tutorials).
So there were some issues with the sample AJAX code.
I need to trigger the code with on a set interval of time.
I don't want this interacting with my textbox at all, just backing it up behind the scenes
The data I wanted to back up would be full of html markup from the rich text editor, I did not trust passing that through GET as I am pretty sure there would be a lot of illegal characters in that URL (GET is a way of passing data that basically puts your variables in the URL of a web page, ie: http://mypage.com/index.php?variable1=value1&variable2=value2)
The PHP worker script would need be changed to collect the data from $_POST and insert it into a MySql database, and then output the last autosave time.
So for starters, I change what was a trigger in the <input> field to be a trigger on load. I had to put this in the header.php of my website as that is where I define <body>, so I added a little if statement to ensure this was only happening on the page where I am working on posts, post.php.
Now I have a function I call immediately when I load up a post creation / edit page. To get the code to repeatedly trigger the asyncronous sending of data I define init() as follows:
That will call the function AutoSave() every five seconds, and that is where I will define my AJAX data send. I hardcode the grabbing of the contents of "textarea1" as that is the only text box I will ever be worried about autosaving.
The setRequestHeader calls at the end of AutoSave() are the method used to pack up my variable, params, into the $_POST data stream. I then call xmlHttp.send with the params var, rather then null, to finalize sending of the data via POST.
Despite my avoiding passing data by GET for fear of unhandled special characters, there are still some characters that confuse the POST data. any & or + symbols would get interpreted as part of the variable defining escape characters. (meaning it looks for & to define additional variables). I also had some trouble with output of "<" characters when debugging to see if i was fetching the data correctly. So to solve these special character issues I added a few lines of string replacement to encode my sensitive characters to something I could easily string replace from the side of autosave.php to decode back to normal.
With all the data collection now handled, I just needed to make sure I was collecting the data correctly from the autosave.php side before I start trying to insert anything into the database.
I also added an output area to post.php at the top left of the RichTextEditor textbox, that would display the last update time, and while debugging would should the data collected.
Now the testing began. First I turned off the Richtext editor as it does strange things with textarea1, turning it in to an <iframe> of sorts, I wasn't sure how that would interact with me constantly fetching the textarea1 contents. Secondly I turned off populating the textarea with contents of the database in the case of editing an existing post, I though that setting the contents of textarea on page load might have some negative impact on trying to fetch the contents later.
With both of these turned off, the autosave works beautifully. I type in some text, it automatically gets captured and appears in my little debug area exactly as I typed it. This looks good, if things keep up I can write some simple SQL and backup logic and be done with this thing. Sadly this is when problems began to arise. While everything was looking fine in IE, in Firefox, every time the ajaxReply area was populated with the "Last autosave compeleted @" string, the whole window would flicker, and the textarea would lose focus and regain focus, setting the cursor to the end of the window. Very bad for a low-impact autosave, the last thing I want is to interrupt a user while they are mid sentence. I did find a solution although it was one I didn't like. As long as I didn't updated the "ajaxReply" area, the page would not flicker and focus would not get lost. I figured giving up my little "last autosave" status message would be ok if it would get it to work.
Farewell my beautiful prompt :'(
The next thing I noticed was that if I turn the database value back on, so I can now see the exisiting content when editing a post, the data I collect in Firefox for backup is ALWAYS the default data. Regardless of what I do to the textarea, even deleting all the contents. For some reason I could not understand, document.getElementById("textarea1").value; always returns the value the textarea starts with on load. I tried various solutions the internet suggested like document.reset() or document.clear(), but Firefox didn't recognize any of these.
All hope wasn't crushed yet however, I still had IE working. So now I turned the Richtext editor scripts back on. Sadly this stopped everything. I did not write the Richtext editor scripts myself but I do have a pretty good understanding of it. It does some sort of replacement effect on the textarea turning it almost in to an <iframe>. This would seem to kill any hope I had of fetching the data from the textarea however.
With that, I decided to scrap the idea. I learned a lot from the experiment, and maybe one day I will revisit it. I did get a sound piece of advice from a colleague however. If all I was worried about was leaving the post page while in the middle of typing, why not simply implement a prompt that asks "Are you sure you want to leave this page"?
This solution was perfect, and in my eagerness to do a lot of fancy work saving the data behind the scenes, I totally over looked it as a possibility. Coding it up proved rather simple:
With a little bit of code in header.php and post.php, I was able to define a toggle-able prompt that would pop up any time you try to navigate away from the page, close the tab the page is in, or even close the browser. The only thing that would cause people to unwillingly lose data now would be power loss, and that I am okay with =P
It was a long fight and despite not succeeding with my original plan, I learned a lot, and added a new feature to the NilsBlogPress suite. Hopefully for those of you that have read this far, you have learned something too.
As I have mentioned in a previous post, I am currently working on a large project to create a Magic The Gathering RPG stylized game. This idea is not at all unique. Magic the Gathering is a well established game, and there have even been RPG magic games in the past. The main one I draw inspiration from is an old Microprose Studios game called "Conquerors of Shandalar":
This game was great. It had a randomly generated overworld map, different towns to purchase cards and get quests, random monster spawns (like the Enchantress above) who would chase after you and challenge you to a duel for Ante (meaning cards from your deck are at stake), and a full fledged AI for in game play.
The draw backs where a large number of bugs, some faulty AI, a sometimes confusing interface, a VERY limited selection of sets available for play, a lackluster story, and no support beyond Windows 98. It is a great sandbox game for wandering around and fighting guys, and they did manage to fit the story in to the Canon of the Magic (right after the Ice Age), but it needs some reworking. This is the inspiration for me to create something new and better then this original. Partially because I want to play a game like this again, and part just to see if I can do it.
So beginning the project, the first step was to plan. First and foremost I wanted to be able to play Magic the Gathering, so I planned out what I would need to learn how to do, and what types of programs/functions I would need in place, to be able to get there. I called these my Piece-wise Programs:
So with that plan set out, I will keep hammering away until I have something useful. For documentation sake I am going to post each Piece-wise Program here, source code and all and give a description of how it works, what it does, and what I learned from it.
For the past two years, I have been frequenting Magic the Gathering sealed/draft tournaments. Wizards of The Coast has set in place a Monthly Player Rewards program that earns players free textless cards and oversized promocards based on the number of sanctioned events they attend. This week I finally got my first MPR cards! The MPR cards are textless cards. When you recieve rewards cards you will get the month's MPR card (DoomBlade) as well as a random MPR card from past a month (Terminate).
If there is a set coming out soon you can get an Oversized promo card. Since Scars of Mirrodin prerelease/release is next week, I got an oversized WurmCoil Engine.
Also since this was my first time receiving player rewards cards I also got a shiny new DCI card. Very handy considering I lost my other DCI card and only had my number in the form of a contact on my phone =P. Now if only I could remember my VERY first DCI number as to merge my older records with this current DCI number.
The other day at work, I was updating a set of PowerPoint
slides for a presentation I had written up a while ago. I came to slide
I had left blank and simply wrote "Demo 1 here". It was the slide where
I would pause, minimize the slide show, and give a demo of the feature I
was presenting on. This worked well and good at the time, as it was just a
one time presentation. But now almost 2 years later the feature is
being revived and this slide deck is going to be circulated across the
team for many more people to learn from. The only problem is that these
"Demo here" slides are a little lackluster with out an actual Demo.
That
is when I took to the internet to search for an easy way to capture
video from a screen, with some optional voice over, and save it as a
movie. I wanted something simple, and didn't want a watermark (like the
http://www.fraps.com watermark you get with the free version of Fraps).
Low and behold I discovered a nice little program suite called Microsoft Expression Encoder 4.
I was able to download the entire suite through work, but I believe
there is a trial version on their page. It has a load of features, far beyond
what I was looking for, but all of which look like something I'd want to
dig in to later. The best part is the easy to use Screen Capture.
With one click I can start recording my actions on screen, save them,
and encode them to .wmv for easy distribution.
This compact little toolbar is what opens up when you open the Screen Capture
The Screen Capture works simply enough, just click
record, specify an area to capture, click record again, and action. With
bindable hotkeys default set to Ctrl+Shift+F11 to start/pause, Ctrl+Shift+F12 to stop, and Ctrl+Shift+F9 to zoom, controlling the capture is easy. You can also specify options like "hide the mouse pointer", or select an audio source to capture along with the track (microphone, speakers, etc.).
(click for larger image)
When the capture finishes, it brings up a list of recent captures (pictured on the left) and gives you the option to send these raw files to the encoder (pictured below). From the encoder there is more editing you can do, but I have yet to really dig in to it. All I've done is hit "Encode Now" to save the video to WMV file type. Then I can easily plug in the now 1-2 Megabyte video into my slide deck and distribute my presentation with demo an all.
(click for larger image)
Best of all though, I look like a super pro employee : )
I will have to revisit this program in the future and try out some of the other nice features. For now I will likely just use it for presentation recordings at work, and maybe even put up some tutorial videos on this blog.
A little over a year and a half ago I started work on a very ambitious project. And while I haven't gotten far towards the end goal, I have made some pretty successful achievements and learned a lot. The project I am talking about is a Magic the Gathering PC game that would bring together:
a Deck Building tool
Game play that enforces rules
Online Game play over IP
Game play vs AI
Card display with the most recently errata'd wordings
and an RPG stylized campaign- game / over world, to tie it all together
Holy hell, ambitious much?
So in all the time I have worked on it, what have I gotten done? Not a whole lot but I'd consider the DeckBuilder to be in a releasable state.
The DeckBuilder started out as a simple card database. I wrote a C# app that parses the Wizards Gatherer website and pulls all the information on every card in every set of every Art (lands, and the Homelands set have multiple arts per card). The information and card images are downloaded and plugged into a relational database that makes it easy to do any query on any part of a cards info. And with all the card information downloaded, there is no need to keep connecting to the Wizards site to get card info.
So with all the info nicely contained in the newly named MTGX.db (the x stands for Xtreeeemmmee!! or expansion, w/e =P), I now had to plot out how the program will build up different SQL queries depending on what information is provided. With lots of trial and error to reduce the run time of the queries, I ended up with a pretty fast deck builder, that lets you add cards from your query results list to a deck building list, and save the information off to a .dec file (.dec does exist somewhere but this is my own implementation, I wanted my own file structure). The finished product is this:
I could go in to quite a lot of depth on everything with building just this (and i plan to in future posts!), but this is simply a status of what I have completed so far in the grand scheme of things. The next course of action, aside some last minute tweaks to the DeckBuilder, will be creating the Game Field and working out how to plug in players to control interact with it.
For now I am happy with what I have, and while there is a lot left to do across multiple fields of expertise, I look forward to the challenge.
I plan to do many more posts on this Project and I will likely start with going through what I have done piece by piece, and then following up with more posts on planning the next steps. I hope if you've read this far you'll check back and lend me some feedback in the future :D