My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Friday, September 22, 2006

F.A.P. - Fast Archive Preview

What is F.A.P. ???
F.A.P. is my archive preview idea, a fast way to know a title and its permanent link of each post and for each month (or date).
You can view an example scrolling this page to Archieves and wait few seconds over one month just with your mouse.


How does F.A.P. work ?
Blogspot is a wonderfull and free service but its server is not available to its users.
There's no way (at least I've not found anyone) to implement ajax request to do some "experiment".
However, we are really lucky because blogspot doesn't block external requests, then every host should read our blog content, simply calling one page.
This was the key to create F.A.P. web service, that's a simple dedicated blogspot output parser.
The called external page will read a built archive page, for example one of the pages you can find under the Archieves on your left or right side of your blog.
After that our external requet page will produce a JSON like output usable for our scripts.


How to implement F.A.P. in your blogspot place ?
The first thing to do is a class or a function that can add dinamically a javascript tag inside page's head tag.
I've used this simple Class to do that

function DinamycScriptInclusion() {
// (C) Andrea Giammarchi - MIT Style Licence - webreflection.blogspot.it
function __add(fileName) {
var js = document.createElement('script');
js.language = "javascript";
js.type = "text/javascript";
js.src = fileName;
__fileList.push(fileName);
document.getElementsByTagName('head').item(0).appendChild(js);
};
function __remove(fileName, i) {
var scripts = document.getElementsByTagName('script'),
j = scripts.length;
while(j--) {
if(scripts.item(j).src == fileName)
scripts.item(j).parentNode.removeChild(scripts.item(j));
};
__fileList[i] = null;
};
this.add = function() {
for(var i = 0, j = arguments.length; i $lt; j; i++) {
if(__fileList.indexOf(arguments[i]) !== -1)
this.remove(arguments[i]);
__add(arguments[i]);
}
};
this.clear = function() {
__fileList.forEach(function(fileName, i){__remove(fileName, i)});
__fileList = [];
};
this.remove = function() {
for(var i = 0, j = arguments.length; i $lt; j; i++) {
if(__fileList.indexOf(arguments[i]) !== -1)
__remove(arguments[i], i);
__fileList = __fileList.filter(function(e){return e !== null});
}
};
var __fileList = [];
};

This class has only 3 public methods
- add, to add one or more external javascript file
- clear, to clear every external javascript file we have add
- remove, to remove one or more external javascript file from pur head

The second thing to do is to create a global variable to save (or cache) requet informations.
The name of this var is, obviously, webreflection :D

webreflection = {};


Now we need to add an onload event because we need to find and modify every Archive Link.

// use your favourite "addEvent" method, this is only an example
onload = function() {

var blogName = "your_blog_name_here", // i.e. blogName = "webreflection"
li = document.getElementsByTagName("LI"),
i = li.length,
link = null,
tmp = blogName.replace(/-/g, "\\-"),
re = new RegExp("(http://".concat(tmp, "\\.blogspot\\.com/)([\\w]+)(_", tmp, "_archive\\.html)"));

// loop over every found LI
while(i) {

// blogspot calls UL Archive container class "archive-list"
// we're interesting only to this container, that should be
// the parent node of one or more LI
if(li[--i].parentNode.className === "archive-list") {

// well, we have found an Archive li
// then we can add a div (unobtrusive layout)
// that will be used with F.A.P.
li[i].innerHTML += "
");
// we can use a class name, then in our CSS div.month-ghost can be as you want

// now we need to get the link inside this LI
link = li[i].childNodes[0];

// and we need to get date inside this link
// stored inside the link as an un-standard parameter
link.webreflection_remstring = link.href.replace(re, "$2");

// we need to save created div too, using unique id created with innerHTML
link.webreflection_div = document.getElementById("month-ghost-".concat(i));

// we must hide created div (then you don't need to write display: none inside CSS)
link.webreflection_div.style.display = "none";

// and we must set link referer too (this.referer shortcut)
link.webreflection_div.referer = link;


// we need to add events to link and to div too to manage F.A.P.

// link onmouseover
link.onmouseover = function() {

// usefull callback
function loaded() {
// if external host has created output
if(webreflection.response) {

// delete the interval
clearInterval(element.webreflection_interval);

// and call monthPreview function
monthPreview(element);
}
};

// element is this link scope inside this function (and loaded function too)
// txt is the date inside the url of this link
// url is the page to call with your blog name and the date as query string
var element = this,
txt = this.webreflection_remstring,
url = "http://win.3site.eu/fap.aspx?blogname=".concat(blogName, "&date=", txt);

// if there's an interval (created by onmouseout)
if(element.webreflection_interval)
clearInterval(element.webreflection_interval);

// if webreflection_div_content parameter is present, we don't need to call external page
// then just show old result
if(element.webreflection_div_content)
monthPreview(element);

else {

// reset old webreflection.response
webreflection.response = null;

// clear old scripts add on head tag
DSI.clear();

// and add created url
DSI.add(url);

// then set the interval to check webreflection.response
element.webreflection_interval = setInterval(loaded, 20);
}
};

// link onmouse out
link.onmouseout = function() {

// function to hide the div
function removeDiv() {
element.webreflection_div.style.display = "none";
};

// element is the this scope for the function (for removeDiv too)
var element = this;

// if onmouseover hasn't load the content, the interval is not 0 (not false)
if(element.webreflection_interval)

// then stop that interval
clearInterval(element.webreflection_interval);

// and call after 500 seconds remove function
element.webreflection_interval = setInterval(removeDiv, 500);
};


// div onmouse over
link.webreflection_div.onmouseover = function() {

// while we go with mouse over the div the link will create an interval
// (onmouseout) to remove this div after 1/2 second ... then stop them
clearInterval(this.referer.webreflection_interval);
};

// div onmouseout over
link.webreflection_div.onmouseout = function() {

// when we leave the div we want to call link onmouseout to hide them
this.referer.onmouseout();
};

}
};
};


I'm sorry for my bad english and I hope you've understund what this script does.
There's just a final function to view, the monthPreview function.

// function to write the content inside the div
function monthPreview(element) {

// div was saved as link.webreflection_div
// content is an array used "as StringBuild" to add content
var div = element.webreflection_div,
content = [];

// if content was loaded
if(element.webreflection_div_content)
// add that
div.innerHTML = element.webreflection_div_content;
// else create the output with webreflection.response array
else {
for(var i = 0, e = webreflection.response; i $lt; e.length; i++)
content.push('<a href="'.concat(e[i].link,'">',e[i].title,'</a>'));
};

// now we can show the div
div.style.display = "";

// and cache its content for next time :)
element.webreflection_div_content = div.innerHTML;
};


Last thing to know is that you can modify your "preview div" layout using css inside your blogspot page model.

To have the web service You can use my space but please, when I'll tell you (give me few days) what does server side script do to create response, use your server not mine :D


Finally, it's the complete script without comments, see you.

1 comment:

Andrea Giammarchi said...

Ooops, sorry guys, DinamycScriptInclusion uses JS 1.6 sintax (I use everytime JS 1.6 sintax ...) because I write everything with JSL at the top of the page :D

However script example has a "boring" Array.prototype.indexOf and some loop changed, then this code should work with your browsers too ...

http://www.devpro.it/examples/fap.example.js