InProgress... The Newsletter
September 09, 2010, 12:37:31 PM *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
News: Leave me a message in the guestbook.
 
   Home   Help Search Calendar Tip Mug Members Login Register  
Pages: [1]
  Print  
Author Topic: WORKING Ajax History and how to implement using CodeIgniter and MooTools  (Read 401 times)
0 Members and 1 Guest are viewing this topic.
mr_coffee
Administrator
*****

Caffeine: +65535/-0
Offline Offline

Posts: 53


there is no such thing as impossible


WWW
« on: March 28, 2008, 12:26:01 PM »

In the case of this site, I used CodeIgniter for a php front-end.  You will need SOMETHING in place that can differentiate between an Ajax call and a regular typed in URL.  In CodeIgniter, you accomplish that by modifying "Input.php" in the libraries folder to include the following:

Code:
function isAjax() {
    return (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH']=="XMLHttpRequest");
}

With this function in place, you can check for ajax in your controller.  For example, say I used to have THIS as my controller for my index file:

Code:
function index() {
$data['title'] = "Home";
$this->load->view('title',$data);
$this->load->view('scripts');
$this->load->view('header',$data);
$this->load->view('navigation');
$this->load->view('home');
$this->load->view('footer');
}

I would replace that code with the following:

Code:
function index() {
if($this->input->isAjax()) {
$this->load->view('home');
    } else {
$data['title'] = "Home";
$this->load->view('title',$data);
$this->load->view('scripts');
$this->load->view('header',$data);
$this->load->view('navigation');
$this->load->view('home');
$this->load->view('footer');
}
}

With PHP prepared, only the requested file will be sent to the browser if it is an Ajax request.  If it is NOT an Ajax request, the entire page will be loaded.  When using CodeIgniter, you'll still want to load your helpers in your default controller, but no views in the case of an Ajax request.  Ajax may be "asynchronous"... but you don't want your PHP sending more than one "view" at a time.  Technically, the page could request as many as it wants at the same time... but you want each view queued properly by the PHP as well.

I use MooTools because it makes the next part easy.  Well, at least easier than finding out by trial and error that none of the other megabyte sized scripts designed to do this actually work.  The ideas behind all of those scripts are identical: 1) check the location bar every second and 2) manipulate the browser history and if IE 3) create an IFrame and manipulate its history.  They all claim to have some unique method for acquiring a backdoor into each specific browser's history so as to manipulate it without throwing security errors.  Since none of them actually work, I don't know about all that.  What I do know is that all browsers less IE is registering your "clicks" as history events, but is not registering any history location.  My solution is to create a history of locations by way of array.

So first off, a few variables:
Code:
/* History Variables */
var baseUrl = "http://mrcoffee.ws/index.php";
var history_man = new Array();
var history_num = 0;
history_man[0] = baseUrl + "#" + "index.htm";

As with all histories, we can only manipulate the "hash" of location if we don't want the browser to load a new page.  With CodeIgniter, I set my default extension to ".htm" but you could have anything here.  You could also eliminate the "index.php" portion using .htaccess, but that would make the hash look kind of funny in my opinion.  These variables set up "page zero" of the history.  The only time this "won't" work is when someone is loading from a bookmark... in which case there will be THIS entry in our history prior to their page load.  That's... actually ok.  Half the browsers won't let them go back to this entry since they didn't click any links... the rest...  who cares if they get to go "back" to our home page before going to the page they saw before visiting if it means the rest of the history works?

Ok, two blocks of code to patch IE for a history:
Code:
var iehistoryfix = function() {
var iehistory = top.document.createElement('iframe');
iehistory.src = 'blank.htm';
iehistory.id = 'iehistoryframe';
iehistory.style.display = 'none';
top.document.getElementsByTagName('body')[0].appendChild(iehistory);
}

var updateiehistory = function(page) {
var ieDoc = window.frames['iehistoryframe'].document;
ieDoc.open();
ieDoc.write('<html><body id="iehistorylocation">' + page + '</body></html>');
ieDoc.close();
}

The first creates our favorite "hack": the IFrame.  The second updates the IFrame.  Neither does anything YET, they have to be called on to do anything.  Do include a "blank.htm" because to not do so will make IE perform stupidly.  My contents of blank.htm are:

Code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><body id="iehistorylocation"></body></html>

Now the location check.  If anyone finds more efficient methods - please let me know, but this is what I've done:
Code:
var loccheck = function() {
var testUrl = (top.location.href == baseUrl ? history_man[0] : top.location.href);
if (window.ie) {
var loc = window.frames['iehistoryframe'].document.getElementById('iehistorylocation').innerHTML;
testUrl = (loc == '' ? history_man[0] : baseUrl + '#' + loc);
}
if ( testUrl != history_man[history_num]) {
if (testUrl == history_man[history_num-1]) {
history_num--;
updateAjax(history_man[history_num],true);
} else updateAjax(history_man[history_num+1]);
}
};

Does all the manipulation of the history, and calls my updateAjax function.  The Ajax request is - under these conditions - a two part process.  Basically a whittling down of what is to be requested, then the request is made.  My "whittling down" part of the function looks like this:

Code:
function updateAjax(requrl,historycall){
if ($defined(requrl)){
// gather 'hash'
var requested = requrl.match(/(?:http\:\/\/mrcoffee\.ws\/index\.php)(?:.)(.+)/)[1];
// gather title
var page_title = choosetitle(requested);

// update hash
top.location.hash = requested;

// update titles
$('logotitle').innerHTML = page_title;
document.title = "InProgress... " + page_title

// update history
if (!historycall) {
history_num++;
history_man[history_num] = baseUrl + "#" + requested;
if (window.ie) updateiehistory(requested);
}

// make ajax request
req.url = baseUrl + "/" + requested;
req.request();
}
}

Step one is pretty obvious... takes the entire address and regex-es out the full url before setting the hash.  I rather suck at regex though so I couldn't figure out how to option to do an "in case" the "www" was still there and fixed that in .htaccess instead, but you might have a method to add that part.  Title function... you should write your own or leave this part out.  Based on the page requested it will update the title of the document as well as the displayed title of the page.  Do that or have the document title formatted somehow to reveal the file name (requested).  If "historycall"... we don't want to add that event into "our" history or there will be more lines in our history than there will be clicks left in the browsers history... basically it starts performing rather strangely... though it was interesting for a minute what pages I ended up on.

The second part is to do the real Ajax thing...
Code:
var req = new Ajax('',{
method: 'get',
update: 'wrapper',
evalScripts: true,
onComplete: function(){ ...
}
});

Do whatever onComplete if anything.  The last and final part will be to modify your Window.addEvent('domready', function().  The links you want involved in the insanity above, you'll want to perform this on:

Code:
$$('a.toolTipElement').each(function(el, i) {
if (i <= 3) {  // if it's not my external links (news.mrcoffee.ws/inprogress.us)
el.addEvent('click', function(e) {
if (e) new Event(e).stop();
updateAjax(el.href);
});
}
});

I added an exception for two external links in my navigation list... oh YEAH... bad form, blah blah blah.  Yeah well my super cool MooTools tool tips sorta warn the user they're about to browse away so hush.  If you allowed CodeIgniter's "anchor" feature to write your links, then they're full direct links (not relative) and that's why there's a regex in my Ajax "whittle down" function.  If you didn't... you should make your links direct and do the regex thing anyway.  Makes search engines happy, serious.

Three more lines to add to Window.addEvent('domready', function() and you're finished:

Code:
if (window.ie) iehistoryfix();  // initiate history for IE
setInterval(loccheck, 1000); // history checking on interval
if (top.location.hash) updateAjax(baseUrl + "/" + top.location.hash.slice(1)); // refresh/bookmark check

The last line makes bookmarks possible.  If someone were to bookmark a page... it will have a hash with this history enabled.  This converts that URL... the "location" into something the Ajax "whittling" function will understand.  So for a split second: the home page will be displayed by CodeIgniter... but this script recognizes that another page is intended and so pulls it up all Ajax like.

You can set a bigger delay, but a second should be as often as you check.  All histories I've looked into do this, some just hide it behind pages of code a little better than others.  I tried some "onclick" events but some browsers ignore them depending on where they are.  If a browser has javascript disabled... none of this happens.  Fortunately the CodeIgniter routing takes over... there wouldn't be any hashes in bookmarks or otherwise, and all pages will be displayed as expected.  In other words: degrades perfectly.  The other uber cool feature of this script - as written - is that it adds a "forward"... which along with "bookmark" and "refresh" is the other thing that each and every one of the existing history scripts are lacking.  The only thing this script does not compensate for is a "flash" history, which I will probably never get into.  The way I see it: Adobe needs to work on that yet.  Since we have to slice and dice any flash movie just to make it XHTML strict and whatnot... I don't expect anything to be permanently effective with their madness.

MooTools v1.11 dependencies:
Window.DomReady
Element.Selectors
Ajax

Everything else will be auto selected in the download, but these are the only three things definitely needed.

Feedback is appreciated!  Enjoy!
Logged
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.9 | SMF © 2006-2009, Simple Machines LLC Valid XHTML 1.0! Valid CSS!