Most read posts this month



Aug 13th 2010 × mootools Request.JSONP to decode shortened url hashes

I tend to get annoyed about URL baiting that takes place over social networks – you really don’t know what’s behind a shortened URL until you click it. Or… until you run a URL resolver that can help. In building my Twitter Trend Aggregator, I came to explore this and found an invaluable service through the API of longurl.org that can uncrunch almost any shortened URL to its original.

Here is the extended Class (http://www.jsfiddle.net/dimitar/3Ntnr/)

Request.longURL = new Class({
    // decode a long url
    Extends: Request.JSONP,
    options: {
        log: !true,
        url: "http://api.longurl.org/v2/expand",
        data: {
            url: "",
            format: "json"
        }
    },
    initialize: function(loc, options) {
        this.parent(options);
        this.options.data.url = loc;
    },
    success: function(data, script) {
        this.parent(data, script);
    }
});
// example usage
var urls = [
    "http://bit.ly/b5Ukzp",
    "http://tinyurl.com/33wz7sv",
    "http://is.gd/e7S7R",
    "http://post.ly/r49y",
    "http://www.google.com/" // won't do anything to it.
];

var output = document.id("output");
urls.each(function(url) {
    new Request.longURL(url, {
        onSuccess: function(data) {
            new Element("a", {
                href: data["long-url"],
                text: url + " -> " + data["long-url"]
            }).inject(output);
        }
    }).send();
});

For a better example of this, visit the Twitter Trends Thingie I wrote and mouseover any encoded URL (or press the ‘Unshorten Tiny URLs’ link in the top right bar).

If you want to just deal with the good old bit.ly, then you can go to them direct like so:

// get all links on page that are bit.ly and decode them
document.getElements("a[href^=http://bit.ly]").each(function(el) {
    var url = el.get("href");
    new Request.JSONP({
        url: 'http://api.bit.ly/v3/expand',
        data: {
            shortUrl: url,
            apiKey: 'R_e744c730bdde44a7eacc88cb892074e7', // change
            login: 'webtogs2' //change this
        },
        onComplete: function(response) {
            el.set({
                href: response.data.expand[0].long_url,
                text: response.data.expand[0].long_url
            });
        }
    }).send();
});

Happy social networking


Jul 28th 2010 × using mootools pseudo selectors to SEO check for hidden page elements

This is an interesting one. Basically, there’s a school of thought in SEO that says, you should not apply a display: none CSS property to elements without a valid reason. By valid, it is understood that the hidden content it is somehow interacting with the user based upon events. For example, flyout / foldout menu systems, mouse tips and so forth are acceptable. Hidden text for the purposes of increasing keyword density and SEO relevance is NOT ok.

There are some sites that claim to check what is detectable and what is not but the bottom line is, only one thing will give you a 100% guarantee and control over what your page is hiding and that is reading it from the DOM directly.

I wrote a little snippet through mootools (as all my sites use mootools) that uses a pseudo selector to search for particular style settings.

*UPDATE* if you found this through some SEO tag/search, you should know that this will only work on pages that utilise the mootools javascript framework (link is on the right handside) and in a browser with a console (chrome, safari or firefox with the firebug plugin). With my “usual” type of readers being web developers, I tend to take this for granted.

// for mootools 1.2
Selectors.Pseudo.style = function(key) {
    var styles = key.split("=");
    return styles.length == 2 && this.getStyle(styles[0]) == styles[1];
};

// output all elements with display: none to console
$(document.body).getElements(":style(display=none)");

Just open your page, open up firebug and the larger command line console and paste (or make a bookmarklet).

Neat or not? You can also look for visibility: hidden or other tricks you can think of (positioning and so forth may need some changes to the code).

note: for mootools 1.3 and slick (still in beta), you need to change this to:

Slick.definePseudo('style', function(key) {
    var styles = key.split("=");
    return styles.length == 2 && this.getStyle(styles[0]) == styles[1];
});

I hope it helps you – it’s a useful trick anyway, you can use this to select all elements with color: red, for example.


Jul 15th 2010 × protecting private class methods in mootools

This is a bit of a sore topic as it is often being asked by people on forums and sites like Stack Overflow. Essentially, javascript does not offer a meaningful way to protect methods from being called directly. Come to think of it, neither does mootools–at least not officially.

However, there is a private API (meaning, not supported) that allows you to do it – its via .protect()

Here is a simple ninja class that shows how we can prevent our ninja from being killed directly without receiving damage first:

var ninja = new Class({
    Implements: [Options,Events],
    options: {
        startHealth: 10,
        name: "Bruce",
        surname: "Lee"
    },
    initialize: function(options) {
        this.setOptions(options);
        this.health = this.options.startHealth;
        this.alive = true;
    },
    heal: function(points) {
        if (!this.alive)
            return;

        this.health += points || 1;
    },
    hurt: function(points) {
        if (!this.alive)
            return;

        this.health -= points || 1;
        if (this.health <= 0)
            this.die();
    },
    die: function() {
        this.alive = false;
        this.fireEvent("death", this); // let everyone know
    }.protect() // protected method
});

var bruce = new ninja({
    onDeath: function() {
        // default handler when death occurs
        alert(this.options.name + " " + this.options.surname + " has passed away.");
    }
});

bruce.hurt(5);
try {
    // not allowed to kill ninja directly, need to hurt it instead
    bruce.die();
}
catch(e) {
    alert("the ninja lives!");
    alert(e);
}

// now hurt it more so it dies anyway
bruce.hurt(5);

The .protect() will cause any attempts to call .die on the instance directly to throw an exception and fail. Once again - although this works with mootools 1.2.4 and on mootools 1.3 nightly build, it's not documented and is unsupported - don't rely on this too much (may disappear in mootools 2.0, perhaps). Still, a nice feature to have at hand, certainly

Check the jsfiddle to play with it some more: http://www.jsfiddle.net/dimitar/jQW5q/


Jul 9th 2010 × Create bit.ly addresses on the fly with mootools and Request.JSONP

I was writing my own re-tweet class and found that I need to crunch URLs on the fly to make them fit within the 140 character limit on Twitter. Come bit.ly and their API, easily accessible via REST and with a JSONP callback interface, just the job for extending Request.JSONP:

Request.bitly = new Class({
    // shorten urls via bit.ly
    Extends: Request.JSONP,
    options: {
        log: !true,
        bitly: {
            api: "R_e744c730bdde44a7eacc88cb892074e7", // GET YOUR OWN API KEY
            login: "webtogs2" // GET YOUR OWN LOGIN
        },
        url: "http://api.bit.ly/v3/shorten?login={login}&apiKey={api}&longUrl={longUrl}&format=json"
    },
    initialize: function(loc, options) {
        this.parent(options);
        this.options.bitly.longUrl = loc;
        this.options.url = this.options.url.substitute(this.options.bitly);
    },
    success: function(data, script) {
        this.parent(data, script);
    }
});

This is the class, now for the example uses, this will convert all links from the page into bit.ly URLs (if they are shorter than the original)

document.getElements("a").each(function(el) {
    new Request.bitly(el.get("href"), {
        onSuccess: function(data) {
            if (data && data.data && data.status_code == 200) {
                var orig = data.data.long_url, hash = data.data.url, difference = orig.length - hash.length;

                if (difference > 0) {
                    el.set({
                        href: hash,
                        title: hash + " - saved " + difference + " chars"
                    }).addClass("shortened");
                }
                else {
                    var error = "ERROR:\n your bit.ly hash '" + hash + "' is longer than the original '" + orig + "'";
                    el.set("html", el.get("html") + error);
                }
            }
        }
    }).send();

});

See it all in action on the little jsfilddle here: http://www.jsfiddle.net/dimitar/xVtZy/


Jul 7th 2010 × code your own facebook ‘be the first to like’ with mootools

I never like having iframes in production sites source codes so when management decided to add the ‘facebook like’ widget thing, I decided to move it outside of the source and into a javascript abstraction. Easiest and most convenient way to do so is via extending the Element prototype in mootools (via Element.implement()):

Element.implement({
    facebookLike: function(url, options) {
        var options = $merge({
            show_faces: "false",
            layout: "standard",
            width: 450,
            action: "like",
            font: "lucida grande",
            colorscheme: "light",
            height: 30,
            href: url
        }, options);

        var src= "http://www.facebook.com/plugins/like.php?href={href}&layout={layout}&show_faces={show_faces}&width={width}&action={action}&font={font}&colorscheme={colorscheme}&height={height}".substitute(options);
        return this.adopt(new Element("iframe", {
            styles: {
                border: "none",
                overflow: "hidden",
                width: options.width,
                height: options.height
            },
            id: "FBFrame",
            frameborder: 0,
            allowTransparency: "true",
            scrolling: "no",
            src: src
        }));
    }
});

// exmaple use:
document.id("like").facebookLike("http://fragged.org/", {
    width: 400,
    height: 30
});

Why would you want to do that instead of the default interface? Because it will speed up page load and abstract this into domready / onload events. It also allows you to produce multiples very easily via a a selector, eg,

// if in your markup you have a number of


// then target them, get the link's href and apply the like button instead
document.getElements("div.like").each(function(el) {
    var urlToLike = el.getFirst().get("href");
    el.empty().faceboookLike(urlToLike);
});

All of the facebook options like button plugin are “supported” – you can find the list here

Finally, here’s what it looks like:

Play with it some more on the jsfiddle: click here


Jun 3rd 2010 × beware: googlebot understands javascript, mootools and ajax now

This came as a complete shock to me: due to the Google Mayday update, I have been monitoring how the bot accesses our sites in a futile search for clues as to the page rankings changes. Very surprised to discover googlebot fetching URLs that are _strictly_ available through Ajax only.

Here is a sample request from earlier today that fetched a small worker thread that produces additional product images:

66.249.65.25 - - [03/Jun/2010:11:25:43 +0100] "GET /angles.php?id=102257&version=Brown HTTP/1.1" 200 801 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"

We have all spoken of Google’s increased capability of following and deciphering javascript logic but it goes beyond the scope of reasonable effort, in my opinion. Is the scraping done based upon URLs detected in the javascript blocks or does it actually evaluate, run and monitor the calls? If the scraping works based upon regex that catches URLs, would obfuscation / base64 encoding help? Does googlebot support XHR via POST or only via GET? Too many questions without an answer at this point, I will update this post as more info becomes available to me.

Such fetches can often be undesirable–certain AJAX calls we do are tied in with sessions and can cause errors / notifications when being requested w/o due cause, session or event. I am currently trying to find if there is a plausible way to apply a “nofollow” to such calls at all that does not involve a lot of editing and blocking of googlebot from any scripts that it has no place reading. If it comes to it, I will revert all ajax handling to a /xhr/ folder and disallow it in robots.txt, wonder what the best practice is…

As for the Mayday Update, the less said about it, the better. I have never seen Google SERPs get things so very wrong – for instance, ranking disabled products with 0 links anywhere on the net that redirect their traffic instead of what used to be the landing pages. Longtail relevance? With 2k organic in-links to the landing page, PR4 and quality content built over 2 years, this is not bizarre enough…


May 7th 2010 × mooPlaceholder: input placeholder behaviour class

Some of the HTML5 features just won’t come fast enough. One such feature is the ability to apply a placeholder input attribute, something like placeholder=’your text’ which takes the text and sets it as a pseudo default input value until it’s focused or changed. Currently (at time of writing), this only works in webkit-based browsers like Chrome and Safari but it’s not difficult to bring it into all other browsers.

Here’s a little class / mootools plugin that I wrote to do it:

var mooPlaceholder = new Class({
    // behaviour for default values of inputs class
    Implements: [Options],

    options: {
        // default options
        htmlPlaceholder: "placeholder", // the element attribute, eg, data-placeholder="MM/YY" -> "data-placeholder"
        unmoddedClass: "unchanged", // apply a class to the unmodded input, say, to grey it out
        parentNode: document, // limit to a particular set of child nodes
        defaultSelector: "input[placeholder]"
    },

    initialize: function(options) {
        this.setOptions(options);
        // detect native support (nothing gets done if true)
        this.nativeSupport = 'placeholder' in document.createElement('input');
    },

    attachToElements: function(selector) {
        // basic function example that uses a class selector to
        var inputs = this.options.parentNode.getElements(selector || this.options.defaultSelector);

        if (inputs.length) {
            inputs.each(function(el) {
                this.attachEvents(el);
            }, this);
        }
    }, // end attachToElements

    attachEvents: function(el, placeholder) {
        // method that attaches the events to an input element.
        var placeholder = placeholder || el.get(this.options.htmlPlaceholder);
        if (this.nativeSupport || !$(el) || !placeholder || !placeholder.length)
            return;

        el.set("value", placeholder).store("placeholder", placeholder);

        // append unmodded class to input at start
        if (this.options.unmoddedClass)
            el.addClass(this.options.unmoddedClass);

        // now cater for the events
        el.addEvents({
            change: function() {
                // when value changes
                var value = el.get("value").trim(), placeholder = el.retrieve("placeholder");
                if (value != placeholder) {
                    // once it changes, remove this check and remove the unmoddedClass
                    el.removeClass(this.options.unmoddedClass).removeEvents("change");
                }
            }.bind(this),

            focus: function() {
                var value = el.get("value").trim(), placeholder = el.retrieve("placeholder");
                if (value == placeholder) {
                    el.set("value", "").removeClass(this.options.unmoddedClass);
                }
            }.bind(this),
            blur: function() {
                var value = el.get("value").trim(), placeholder = el.retrieve("placeholder");
                if (value == placeholder || value == "") {
                    el.set("value", placeholder).addClass(this.options.unmoddedClass);
                }
            }.bind(this)
        });
    }
});

// example use for a single element:
new mooPlaceholder().attachEvents($("searchfield"), "type here to search site..."); // override placeholder

new mooPlaceholder().attachEvents($("searchfield")); // use placeholder= value instead

// example use for any input element that has a placeholder property:
new mooPlaceholder().attachToElements(); // defaults to "input[placeholder]"

// or a custom selector
new mooPlaceholder().attachToElements("input.foo");

// or only inputs of a particular form with a particular class (required)
new mooPlaceholder({
    parentNode: $("mysignupform"),
}).attachToElements("input.required[placeholder]");

Clearly, many ways to use it. It will only work as progressive enhancement and won’t attach anything if the browser already supports placeholders.

Make sue you set the CSS class for the unmodded input (look at the CSS tab on JSFIDDLE for an example) – it seems that natively, the text goes to #A9A9A9 but you may be able to override that. Vendor specific CSS styles allow you to work with this:

input.unmodded::-webkit-input-placeholder {
    color: rgb(100, 0, 0);
}

Have fun.


Apr 28th 2010 × mootools 1.3 beta 1 is finally out for grabs

In what promises to be the most significant update thus far (well, perhaps the mootools 1.11 to 1.2 is still worth mentioning as a milestone), the mootools team have finally released a public beta.

Why is it significant? You can read the blog post but here is the short summary: in addition to being set as the groundwork for the transition to mootools 2.0, here are the most significant changes I have found out about (list will expand as I find out more):

  • The introduction of slick (new css pseudo selector engine) – awesome news for DOM manipulation speeds
  • Move away from `Native` to `Type` (semantics) – internal change mostly, unless you hack into Natives
  • Move away from $names – all things like $chk, $type, $A and so forth are now being renamed to plain function names that make more sense and look more javascript-y.
  • Move away from feature detection where possible and a larger reliance on UA parsing instead – not sure I appreciate this as I do not trust UA strings at all, not least of all due to the fact that I often browse with the googlebot UA string when chasing SEO issues around our competitors. There was a nasty post by slicknet a while back in which he ripped into mootools for still using feature detection but I fail to see his point (just as many others do). Both are somewhat unreliable but one does not allow for the user to directly interfere with the framework behaviour.

Additionally, it seems as if the framework has been largely rewritten – most of the source code is totally new to me and it will take me some time to get a better understanding on how it works. Thus far, there’s been no mootools-more 1.3 so I suppose Aaron Newton will be working hard on releasing it soon.

The new documentation is live (courtesy of fakedarren) here: http://mootools.fakedarren.com/docs/core

P.S. Oskar reported earlier via twitter that JS Fiddle has updated and included the beta as framework option so you can play with it.

The


Mar 31st 2010 × mooSelecta – an accessible mootools select/dropdown element replacement

Don’t you just love how different browsers style (or rather, don’t style) select and option elements? One of the things I dread most as a developer is when the design team come in and produce forms with controls that are totally bespoke and unobtainable due to the browser limitations.

This prompted me to write a mootools class that replaces the existing select items on the page with custom style-able controls through CSS as a progressive enhancement. Here is an example of what the resulting dropdowns can look like:

mooSelecta dropdowns screenshot

The idea of skinnable dropdowns is nothing new but not many solutions are done in a way that mimics the select element’s behaviour and events correctly. Here is what mooSelecta does feature:

  • support for any type of skins, all through CSS
  • support for native element events (will work with scripting)
  • support for keyboard look-ups when dropdown is open
  • support for input labels
  • support for tabindex

Have a look on the mooSelecta example page or on the mootools forge page for the plug-in itself (download etc).

One thing you need to be aware of is that if you want to dynamically change the selected value of a skinned select, you need to fire it’s onChange event for mooSelecta to update. For example, if you have a dropdown with id=”country” and class=”selecta”:

new mooSelecta({
    selector: "selecta"
});

// change default selected value via js later:
$("country").set("value", "United Kingdom").fireEvent("change");
// the event will make mooSelecta update

Aside from that, it’s fine, tested and working in IE6, IE7, IE8, Firefox 2, Firefox 3.6, Safari 4, Chrominum 5. Known issues with Opera keyboard events not being raised as expected. Enjoy!


Mar 22nd 2010 × Another javascript test

This time I found a fun javascript test on StackOverflow on the PerfectionKills blog: Javascript Quiz, once again it has been suggested to use through the interview process for a javascript / front-end engineer role.

Good luck and have fun (as they say…)