Most read posts this month



Mar 11th 2010 × porting element storage from mootools 1.2 to 1.11 and 1.1.2

This is a very nice feature, one of the great ideas that came with mootools 1.2. You can store any property within elements by simply mimicking the markup of <div myprop=”myvalue”> in javascript. However, setting such proprietary element properties in IE can be very slow. What mootools 1.2 did was to abstract element storage into a global object with the elements’ UID as key. Very smart, particularly as it allows for storing not only simple text assignments but even references to classes / instances, arrays and so forth.

Unfortunately, some of us (like the Joomla users) are stuck with using mootools 1.1.x for the time being and cannot take advantage of the element storage system. I am so used to keeping data contextual to elements within their storage that I ported the whole thing from 1.2. Here is all you need to achieve the .store and .retrieve element methods:

(function() {
    // compatibility layer with mootools 1.2 element storage system
    // scoped
    var storage = {}, Native = {
        UID: 1
    };

    var $uid = (window.ie) ? function(item){
        return (item.uid || (item.uid = [Native.UID++]))[0];
    } : function(item){
        return item.uid || (item.uid = Native.UID++);
    };

    var get = function(uid){
        return (storage[uid] || (storage[uid] = {}));
    };

    Element.extend({
        retrieve: function(property, dflt){
            if (!this.uid)
                $uid(this);

            var storage = get(this.uid), prop = storage[property];
            if (dflt != undefined && prop == undefined) prop = storage[property] = dflt;
            return $pick(prop);
        },
        store: function(property, value){
            if (!this.uid)
                $uid(this);

            var storage = get(this.uid);
            storage[property] = value;
            return this;
        }
    });
})();

To see this in action, play with my simplified jfiddle example below:


Mar 10th 2010 × Cross-domain AJAX calls via YQL as proxy and mootools JSONP

I wrote this a while back when I needed to do pseudo AJAX calls to a remote host. Due to XSS restrictions and security policies, a normal XMLHttpRequest (XHR) call is not allowed to work across domains or even sub-domains. But Yahoo’s YQL interface kindly lets you GET any URL (which also means being able to submit via GET), irregardless of whether the domain is local or not.

Here is how it works. You need mootools (also download mootools-more as this class will extend Request.JSONP which is a part of mootools-more).

Request.YQLajax = new Class({
    // gets basic info such as country and latitude data
    Extends: Request.JSONP,
    options: {
        log: !true,
        url: "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20html%20where%20url%3D%22{location}%22&format=xml"
    },
    initialize: function(location, options) {
        this.parent(options);
        if (!location)
            return;

        this.options.url = this.options.url.substitute({location: encodeURIComponent(location)});
    },
    success: function(data, script) {
        this.parent(data, script);
    }
});

// example use to fetch BBC's page
new Request.YQLajax("http://www.bbc.co.uk/", {
    onSuccess: function(data) {
        $("result").set("html", data.results);
    }
}).send();

You can see this in action on mooshell or play with it below. Happy cross-domain ajax-ing.


Mar 4th 2010 × Getting latest tweets through mootools JSONP

Nothing like being egocentric so here’s what you can do to add latest tweets of a particular user to your site through mootools. This class totally `forked` from a post by AppDen (Scott Kyle) so feel free to give him some love, he deserves it for being such a bright spark. You can also “play” with this live on JSFiddle

Request.twitterAPI = new Class({
    // return json data with latest tweets form a particular user
    Extends: Request.JSONP,
    options: {
        target: $empty(),
        tweetBits: {
            textClass: "tweetBody",
            dateClass: "tweetDate"
        },
        maxTweets: 4,
        autoLink: true,
        url: "http://twitter.com/statuses/user_timeline/{user}.json"
    },
    initialize: function(user, options) {
        this.parent(options);
        this.options.url = this.options.url.substitute({user: user});
        this.element = document.id(this.options.target);
        if (!this.element)
            return;

        this.element.set("html", "Loading tweets...");
    },
    success: function(data, script) {
        this.element.empty();
        this.parent(data, script);
    },
    showPost: function(post) {
        var creationDate = Date.parse(post.created_at).format("%x %X");
        new Element("div", {
            "class": this.options.tweetBits.textClass,
            "html": this.options.autoLink ? this.linkify(post.text) : post.text
        }).adopt(new Element("div", {
            "class": this.options.tweetBits.dateClass,
            "html": creationDate // + " via " + post.source.replace("\\",'')
        })).inject(this.element);
    },
    addPosts: function(response) {
        if (!response.length || !this.element)
            return;

        response.each(function(post, i) {
            if (i < this.options.maxTweets)
                this.showPost(post);
        }.bind(this));
    },
    linkify: function(text){
        // modified from TwitterGitter by David Walsh (davidwalsh.name)
        // courtesy of Jeremy Parrish (rrish.org)
        return text.replace(/(https?:\/\/[\w\-:;?&=+.%#\/]+)/gi, '$1')
            .replace(/(^|\W)@(\w+)/g, '$1@$2')
            .replace(/(^|\W)#(\w+)/g, '$1#$2');
    }
});

// create an instance
new Request.twitterAPI("D_mitar", {
    target: "myTweets", // target element to 'host' the tweets
    onComplete: function(data) {
        this.addPosts(data); // add all posts to element.
    }
}).send();

Additionally, you can style the tweets by using 2 classes for the element:

div.tweetBody {
    line-height: 1.1;
    font-family: arial;
    font-size: 12px;
    margin-bottom: 10px;
}

div.tweetDate {
    font-size: 10px;
    color: #500;
    text-align: center;
    padding-top: 4px;
}

That’s it, it’s that simple. Happy tweeting.


Feb 14th 2010 × Detect adblocking software through mootools and have a fallback strategy

Being able to check if website adverts were blocked is of some importance. As browser plugins such as AdBlock Plus and the likes are getting better all the time, less and less of your adverts get served up. Hence, my simple solution that can try and recover some of the lost revenue.

Google adwords code can be wrapped into a plain DIV tag without a set width/height, letting any content injected into it by Google dictate its size. Through a basic Element prototype shortcut, called Element.adCheck(), you can check if the size has remained less than expected (aka, the AD got blocked).

Element.implement({
    adCheck: function(options) {
        var options = $merge({
            delay: 3000,
            message: "<center>It looks as if my Adwords did not display<br />I am just a programmer that needs to feed their hungry child and wife, please disable AdBlock or whatever clever advert blocking software you are running on my site.</center>",
            minHeight: 20,
            minWidth: 20
        }, options);

        this.store("timer", (function() {
            var coords = this.getCoordinates();
            if (coords.height.toInt() < options.minHeight || coords.width < options.minWidth) {
                this.set("html", options.message).fade(0, 1)
            }
        }).delay(options.delay, this));
        return this;
    }
});

// check it for a div with id="as":
$("as").adCheck();

// check it for all divs with class 'myads'
$$("div.myads").adCheck({message: 'please disable adblock'});

// use alternative adverts instead
$("as").adCheck({message:"<a href='/signup.php'><img src='/img/signupbanner.gif'>"});

To run this, you need mootools 1.2+ core only. You could also extend the above code to .pass() a function callback so you can execute a proper ‘onAdvertFailed’ event.

Just a thought, but wouldn’t it be nicer if AdSense could serve adverts through JSONP that you can parse and embed clientside? It won’t be mainstream but it could really screw with current AdBlocking methods…


Feb 11th 2010 × Get a url’s tweet / retweet count via tweetmeme api and mootools JSONP

I needed to get retweet counts as data, as opposed embedding the standard widget that tweetmeme supply.

Turning to their API instead got me joy initially. After a stressful few minutes with a javascript label exception was taking place, it transpired that the tweetmeme API was not returning valid JSON within i’s deignated callback wrapper. Instead, it came back as simple JSON. I was about to give up and write a PHP/curl wrapper when I noticed their ‘format’ is called jsonc whereas I was requesting ‘json’. Oh well, works fine now.

Through extending the mootools’ Request.JSONP class, it’s a manner of seconds to get things going:

Request.tweetmeme = new Class({
    // return json data with extended information of a place / location.
    Extends: Request.JSONP,
    options: {
        url: "http://api.tweetmeme.com/url_info.jsonc?url={location}",
    },
    initialize: function(location, options) {
        this.parent(options);
        this.options.url = this.options.url.substitute({location: location});
    },
    success: function(data, script) {
        this.parent(data, script);
    }
});

// example use
// get retweets for all posts on a page and update the counter. URL in the rel property of the markup.
$$("div.twitterBar").each(function(el) {
    var url = el.get("rel");
    new Request.tweetmeme(url, {
        log: true,
        onSuccess: function(data) {
            el.set("html", data.story.url_count);
        }
    }).send();
});

I hope this helps somebody – once again, how easy it is to extend JSONP and get things done in mootools.


Nov 24th 2009 × Lemmings javascript sprite animation with pseudo parallax (through mootools)

This was supposed to be my Friday Fun post but it got to where it’s at on the following Monday instead. Having never tackled sprite animation before, this has been a learning experience anyway (on the principles of animation also).

I think the result is fairly adequate – a moving lemming in a world that changes atmospheric effects dependent on the time of day, complete with parallax-like effect relative as counter to the lemming’s movement.

Oh, yes. The lemming can also be quite moody–hates being bothered. Which is fine, you can blow him up too. Enjoy and click below:
lemmings parallax animation screenshot

P.S. This will feature as the header of fragged.org’s new wordpress theme which is almost completed now. So, a little taster of things to come…
P.P.S. Thanks to crisp for the original DHTML lemmings and to holy.pt for his vigorous testing and advice.


Nov 18th 2009 × Snow flakes for your site? Try mooSnow – a javascript class that just snows

Just pulled out the old textSnow class foer 1.11 and rewrote it for 1.2.4. Anyway, head over to the demo page, it has all the info you need.


Oct 20th 2009 × mootools 1.2.4 released. finally.

Only 2 weeks late, which by software development standards is ‘dead on’, mootools 1.2.4 and 1.1.2 are officially released.

Amongst the more-exciting changes for me (aside from the the many fixes and additions to -more), JSON.stringify and JSON.parse native methods and DomReady always firing before load event (this had been plaguing me for a while on IE).

And, last but not least, a Depender class for lazy-loading of required components that can get your class working. Brilliant – on-demand javascript. All-in-all, a very nice and worthwhile drop-in replacement and enhancement over the old 1.2s.

Just a word of mention, 1.1.2 is a fix in the mootools 1.11 branch that will enable IE8 and gecko (firefox) detection. Not bad…


Sep 25th 2009 × Extending the Request.JSONP class to fetch geolocation data from YQL and others

Here’s a novel idea (stolen from AppDen’s Twitter feed fetch class) – extend the mootools-more’s JSONP class and setup some quick and easy geolocation lookups through pidgets.com and YQL (yahoo query language).

Here they are, 3 mini classes. First one uses the client’s IP address (or an optional ip supplied by options) to fetch geolocation info from http://geoip.pidgets.com/. The second instance fetches extended information on a place / city by location from Yahoo’s geoplaces DB, including things like local district, council authority, county and so forth, something that I find useful when creating quick signup forms for users and PAF lookups are not in the budget. The final class was simple test to fetch relevant timezone info of any latitude + longitude combination. It offers local time, offset from GMT or DST etc, but nothing that Date.get(“gmtoffset”) can’t accomplish anyway).

Request.geoLocation = new Class({
    // gets basic info such as country and latitude data
    Extends: Request.JSONP,
    options: {
        ip: $empty(), // default is user but you can lookup anything.
        url: "http://geoip.pidgets.com/?format=json",
    },
    initialize: function(options) {
        this.parent(options);
        if (this.options.ip.length)
            this.options.url = this.options.url += "&ip=" + this.options.ip
    },
    success: function(data, script) {
        this.parent(data, script);
    }
});

Request.getPlaceInfo = new Class({
    // return json data with extended information of a place / location.
    Extends: Request.JSONP,
    options: {
        url: "http://query.yahooapis.com/v1/public/yql?q=select * from geo.places where text='{location}'&format=json",
    },
    initialize: function(location, options) {
        this.parent(options);
        this.options.url = this.options.url.substitute({location: location});
    },
    success: function(data, script) {
        this.parent(data, script);
    }
});

Request.timeZone = new Class({
    // return local timezone related date from geo coordinates
    Extends: Request.JSONP,
    options: {
        url: "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20xml%20where%20url%3D'http%3A%2F%2Fws.geonames.org%2Ftimezone%3Flat%3D{latitude}%26lng%3D{longitude}'&format=json"
    },
    initialize: function(latitude, longitude, options){
        this.parent(options);
        this.options.url = this.options.url.substitute({latitude: latitude, longitude: longitude});
    },
    success: function(data, script) {
        this.parent(data, script);
    }
});

Here is a quick example of how it can be used on a form. Note you need mootools-more with JSONP for it to work.


Sep 21st 2009 × jsPrintSetup: an AMAZING plugin for firefox that gives extended javascript controls to the printer

This is not a tool you can rely on for customer-facing development, but I found it invaluable nevertheless. jsPrintSetup is a small fireFox add-on that hands you a controlling object in javascript. You get FULL access to any print-related function you could think of: list printers, select default printers, print silently, control spool, headers… There’s just too much to mention. Very useful when coding a CMS that needs to print specific things to specific printers (for example, invoices on printer #1 and labels on printer #2).

Here is an example that fetches a list of printers and remembers the user’s preference for future use:

// only do something if the plugin is live
if ($type(jsPrintSetup)) {
    var printers = jsPrintSetup.getPrintersList().split(","); // get a list of installed printers

    // no print dialogue boxes needed
    jsPrintSetup.setSilentPrint(true);

    // create a dropdown
    var printList = new Element("select", {
        id: "printerList",
        events: {
            change: function() {
                Cookie.write("defaultPrinter", this.get("value"), {
                    path: "/admin/",
                    duration: 365
                });

                // save the new printer selection
                jsPrintSetup.setPrinter(this.get("value"));
            }
        }
    }).injectTop($("printSetup"));

    // get default by preference if any
    var defaultPrinter = Cookie.read("defaultPrinter");

    printers.each(function(el) {
        new Element("option", {
            value: el,
            text: el,
            selected: (defaultPrinter == el) ? "selected" : false
        }).inject(printList);

    });

    // attach an event that prints from an element called goPrint
    $("goPrint").addEvents({
        click: function() {
            jsPrintSetup.print();
        }
    });
}