Most read posts this month



Oct 30th 2008 × slidingTips: a mootools apple-style tooltip effect

This is not working since blog upgrade, looking into it

Seems that Apple really inspire design… After having done largerBox: a mootools apple-style modal lightbox effect, I thought I’d also do some animated tooltip effects similar to the ones on their site. So, I finally got around to it and just drafted an element method for the fragged tooltips (called, slidingTips or slidingTips) into my dci_core.js collection.

Not much more to say, here it is in action:

You can interact with all form elements in this div to get a tooltip

show on focus / hide o n blur:

show on click / dispose after 5 seconds:

show on mouseover / hide on mouseout:

The code? First, the Element.implement bits, 2 functions:

Element.implement({
    slidingTip: function(what, options) {
        // apple style tooltip
        var _this = this, coords = _this.getCoordinates(), options = $merge({
            eventEnd: "mouseleave",
            eventEndTrigger: _this,     // parent element or self
            topOffset: 90,             // offset when in full view
            topStartOffset: 100,        // offset for animation start
            opacity: .8,                // target opacity in full view
            className: "tooltip",       // linked to css
            morphOptions: {
                duration: 300,
                transition: Fx.Transitions.Sine.easeOut,
            },
            delay: 0,                    // can dispose on a timer, in seconds
            id: "tid" + $random(100, 1000)
        }, options);

        // we only need one tip instance per parent element.
        if ($type(this.tip) != "element") {
            this.tip = new Element("div", {
                "class": options.className,
                id: options.id,
            });
        }

        // if it's an existing one, with an implicit id, pick it up
        if ($(options.id))
            this.tip = $(options.id);

        // set initial options and html
        this.tip.set({
            opacity: 0,
            html: "<div style='padding:20px'>" + what + "</div>",
            styles: {
                left: coords.left + coords.width / 2
            }
        }).inject(document.body).removeEvents();

        // show it. cancel any previous animation instances for tip
        if ($type(this.morph) == "object")
           this.morph.cancel();
        else
            this.morph = new Fx.Morph(this.tip, options.morphOptions);

        // show it. really.
        this.morph.start({
            opacity: options.opacity,
            top: [coords.top - _this.tip.getSize().y - options.topStartOffset, coords.top - options.topOffset]
        }).chain(function() {
            // once done, decide how to exit
            if (options.delay == 0) {
                // based on an event.
                options.eventEndTrigger.addEvent(options.eventEnd, function() {
                    _this.morph.cancel();
                    _this.slidingTipaway();
                });
            }
            else {
                // based on a timed delay
                (function() {
                    _this.slidingTipaway();
                }).delay(options.delay);
            }
        });

        // define the tooltip close animation morph object
        var closeAnimation = {
            opacity: 0,
            top: coords.top - _this.tip.getSize().y - options.topStartOffset
        }

        // ... and save it within the parent element
        this.store("closeAnimation", closeAnimation);

        return this;
    },
    slidingTipaway: function() {
        // animate an slidingTip, opposite on in method.
        if (this.tip) {
            this.morph.start(this.retrieve("closeAnimation"));
        }

        return this;
    }
});

There is some CSS to this:

<
div.tooltip {
    background:transparent url(/images/tooltip-ie.gif) no-repeat scroll 50% 50%;
    height:90px;
    left:50%;
    margin-left:-151px;
    position:absolute;
    text-align:center;
    top:0;
    width:299px;
    z-index:1010000000;
    color: #327bb2;
    font-weight: bold;
}

And finally, the demo code:

window.addEvent("domready", function() {
    $("demofocus").addEvent("focus", function() {
        this.slidingTip("Please enter your first name and your surname", {
            eventEnd: "blur", // on blur, remove tip
            topOffset: 82 // tuck in more (heigh is 90 usually)
        });
    });

    $("demosubmit").addEvent("click", function(e) {
        e.preventDefault(); // stop submit from happening
        this.slidingTip("Sorry, you are required to complete all fields before you can submit", {
            delay: 5 * 1000, // remove tooltip after 5 seconds
            topOffset: 82
        });
    });

    // find all .demo_mouse class elements and tooltip them to show data-tip values
    $$(".demo_mouse").each(function(el, i) {
        el.addEvents({
            mouseenter: function() {
                this.slidingTip(this.get("data-tip"), {
                    id: "demoid"+i, // can assign implicit ids to shadows for scripting
                    morphOptions: {
                        duration: 600
                    }
                });
            },
            click: function(e) {
                // prevent the submit from working....
                e.preventDefault();
            }
        });
    });

});

Now, the fragged apple slidingTips currently uses this image:

tooltip ie friendly

This is a .GIF so it will be IE friendly (as opposed to a .PNG with transparency). Alpha blending/anti aliasing is not perfect but will work on a light background (hence the white around the demo code).

This one:

tooltip ie friendly

... way nicer. But, on IE7 even (which allegedly supports transparency in PNG), there is a nasty bug and alpha bleeding near the pointy arrow. You can try using it by changing the .tooltip css class.


Oct 24th 2008 × mooShadow: a mootools CSS based drop shadow for any absolutely positioned elements

Sometimes, you may want to create a shadow effect of a rectangular window/div/image Element and not have to worry about setting it up. We cannot rely on filter effects as it’s not cross-browser compliant. We don’t want .png with transparencies because it does not work well in IE6.

And last but not least, cleaning up the shadow as you remove/dispose the parent element should be automatic. So, we are going to be relying on pure CSS and drawn elements to do this instead, here is our shadowy snippet mooShadow in action:

Element.implement({
    smartDispose: function() {
        // dispose of an element and its dropShadow (if there is one)
        var rel = this.get("data-related");
	    if ($(rel)) {
	        $(rel).dispose();
	    }
	    this.dispose();
	}, // end smartDispose
	dropShadow: function(options) {
	    // creates a shadow effect to a rectangular element

	    // define defaults
        var options = $merge({
            id: "dropShadow" + $random(100,1000),
            x: 3, // offset from parent
            y: 3,
            border: "1px solid #000",
            background: "#555",
            opacity: .5,
            zIndex: this.getStyle("z-index").toInt() - 1 // behind parent
        }, options);

        // only apply shadow on absolutely positioned elements
        if (this.getStyle("position") != "absolute")
            return this;

        var c = this.getCoordinates();

        new Element("div", {
            id: options.id,
            styles: {
                position: "absolute",
                left: c.left + options.x,
                top: c.top + options.y,
                width: c.width,
                height: c.height,
                background: options.background,
                zIndex: options.zIndex
            },
            opacity: 0
        }).injectBefore(this).fade(0, options.opacity);

        // store the shadow id into the element
        this.set("data-related", options.id);

        return this;
    } // end dropShadow
});

We extend all Elements by adding a .dropShadow() method. Here is an example use that’s similar to a tooltip:


window.addEvent("domready", function() {
    $("runShadow").addEvent("click", function() {
        var c = this.getPosition();
        new Element("div", {
            "class": "cur",
            styles: {
                border: "1px solid #000",
                background: "#ffffcf",
                position: "absolute", // required
                left: c.x,
                top: c.y+16,
                width: 300,
                height: 40,
                zIndex: 100,
                padding: 2,
                "font-weight": "bold"
            },
            html: "Example info box with a dropshadow, click to close",
            events: {
                // we close
                click: function() {
                    // close it via our new method, collecting dropshadow as garbage
                    this.smartDispose();
                }
            }
        }).inject(document.body).dropShadow(); // inject into dom and add shadow
    }); // end click event
});

You may want to Run this example


Oct 22nd 2008 × mootools fromCheck: a form validation class

Just saw a really nifty little implementation of a form verification class, check out the demo here: formCheck v1.4. Requires: mootools 1.2


Oct 21st 2008 × An interesting concept: CSS frameworks and SEO source ordered content shuffling

It makes sense to standardise your CSS layout and styles development just as you come to rely upon frameworks for javascript and PHP, I guess. I came upon Elements V2, a nice and tight CSS framework that does not try to do too much but does it well.

I you are in a hurry to produce a new site, it can help you get started on the right path by literally laying down the framework for your content. There is always the danger of such projects to try and be too-helpful at times–the included Lightbox/prototype is overkill. But the mass CSS reset seems to standardise and level the playing field for all browsers so it’s worth grabbing just for that feature alone. Naturally, not a first: css reset.

And of course, from a SEO standpoint – you can then take Elements V2’s layout, move main content div to the top of the source code and the header div to end of the file. You then reposition via CSS and some nested layers… The end result? First bit of text that Google sees on the page is the specific body content – H1/2, titles and so forth. Bits that are repetitive like header and side menus simply do not need to be there at all. This technique is also known as Source Ordered Content or content before navigation. Here is an example SEO content shuffle on a search engine optimisation project I am tinkering with.

A snippet of this non-linear approach to source ordered content content shuffling (takes a while to get your head around the idea):


...
</head>
<body>
    <!--Main Container - Centers Everything-->
    <div id="mainContainer">
        <!--Main Content-->
        <div id="mainContent">
            <div class="content" id="main">
                <h1>Welcome to <strong>ASAP Cleaners</strong>...
                ...
                ...
            </div>
            <!--Footer-->
            <div id="footer">
                ...
                ...
            </div>

        </div>
        <!--Header-->
        <div id="header">
            ... links here
            ... more links
        </div>
...

and here is what it looks like in reality:

after SEO shuffle: reversed layer order

I am not going to go into details and bore you with tedious explanations on how to float elements. Just visit the source ordered content example and view the source.

To an extent, I already do this on the http://fragged.org blog itself – what you see as left side menu actually follows behind all the posts in the source code. I cannot really measure how this works in comparison to a normal linear source. I guess some experimentation will be required but that’s for another post :)

Word to the wise: if your pages’ source code gets to be quite big in size, you may consider not using the “content before navigation” method as your page links may be disregarded by some crawlers (with a 100k or so limit for source size on each page). Also, there is an accessibility consideration–some say source ordered content is being harsh on people with disabilities (er, blind) who use reader software (it cannot find the menus, allegedly) so be careful.


Oct 19th 2008 × YSlow and mod_rewrite: helping in site speed profiling and optimisation

I have been looking around a fair bit recently in a bid to improve performance on a number of sites… And naturally came upon YSlow for FireBug. This is a plugin that does website component profiling based on analysing the performance of all elements loaded into DOM. It examines headers, compression, size, response time, content types and can produce an accurate depiction of your worst performing scripts, stylesheets or images.

It also has a great advisory feature that uses YUI’s best practices in website optimisation (such as, gzipping components, using CDNs, marginalising background images, minimizing requests, to name but a few). It measures and ranks your site and makes you want to tweak it and improve…

I had a long play with various ways of improving but eventually came get to the best results (as speed and size gains) via some mod_rewrite .htaccess trickey:

## enable gzip page compression!
php_flag zlib.output_compression On
php_value zlib.output_compression_level 5

# compress all text & html:
AddOutputFilterByType DEFLATE text/html text/plain text/xml

<Files *.js>
SetOutputFilter DEFLATE
</Files>

<Files *.css>
SetOutputFilter DEFLATE
</Files>

# set clientside cache per element type
# 1 YEAR
<FilesMatch "\.(ico|pdf|flv)$">
Header set Cache-Control "max-age=29030400, public"
</FilesMatch>
# 1 WEEK
<FilesMatch "\.(jpg|jpeg|png|gif|swf)$">
Header set Cache-Control "max-age=604800, public"
</FilesMatch>
# 2 DAYS
<FilesMatch "\.(xml|txt|css|js)$">
Header set Cache-Control "max-age=172800, proxy-revalidate"
</FilesMatch>

This is a combination between cache controls and compression that you can tweak and deploy as needed. If you have relatively static .html or .php files, you can also try adding |html|php or whatever other extension you may see fit. Have fun.

no

Oct 19th 2008 × CubeCart: a ticking timebomb that goes off 1 second at a time

There are certainly some positive things that can be said about CubeCart, a low-end budget entry choice for an e-commerce platform. It’s certainly capable of doing the rudimentary functions it’s supposed to do: lets you add products into categories and lets customers look at them (even buy some!). It also supports a number of popular payment gateways / merchant accounts. Not rocket science.

My gripe with it starts at the price tag: $179.95. You don’t get a lot for your money that you can’t get for free elsewhere (eg OS Commerce, Magento, BossCart JV, VirtueMart, Mambo… to name but a few). In fact, you get nothing.

Irregardless, let’s assume–you are a self styled web 2.0 start-up that wants a piece of the pie, but with limited or no technical knowledge, scant time and resources. And a tight budget (those VC must be sleeping). You have splashed cash on cubeCart and have spent considerable time skinning the shop and populating it with your product range. It’s time to look at the business logic behind your shopping process, the SEO, the landing pages, the sizing and shipping options… And to discover that most of these trivial functions are available as paid-for (as in, you have to pay afresh) ‘community’ add-ons. Should you decide to display products based on brand/manufacturer, that’s also a commercial add-on. You get the picture? It’s a scam. By this time you’re well over your budget and behind schedule so you figure, stopping now means it’s all gone to waste. But perhaps you should, because CubeCart is certainly full of nasty surprises. Read on…

I had the dubious “pleasure” of supporting and improving a CubeCart 4.2.0 shop. Having had to implement and skin a ton of buggy or not totally adequate plugins that were purchased, I discovered another negative side to all of this: the CubeCart modders tend to base64 encode all of their work. That’s right, you can get shipping by country and you can pay for it but by god, you can’t change it. Which would have been ok if any of those fine developers actually had a clue and understood how an e-commerce business operates. Experience has taught me one thing: e-commerce is not what about what a programmer thinks it should be. you don’t just measure the strength of your platform by the number of product attributes or skins you can apply. It is also about making the shop owner’s job as easy and as organised as possible. What does that mean?

For instance… viewing orders into the main admin window or even as popups into a new window is cumbersome and difficult to do especially if you get more than, er… 1 a day. Imagine 40 orders waiting to be shipped, your couriers arriving in 30 mins, your phone ringing off-the-hook, new orders coming in, RMAs arriving and replacements needing to going out… and how do you manage this? Certainly not by looking at the CubeCart orders list, unless you have the memory of an elephant and can remember the contents of each order just by glancing at its order ID or customer name. Of course, you can just click into an order and then see the items contained within, go to your warehouse, find the item, pack it, get back to the PC, print the invoice, login to your couriers’ site, print the label, affix label to box and set aside for pickup. 39 more to go, go back to the orders screen and see if you remember where you were…

Nevermind, all of this can be fixed eventually by a consultant like myself a process that makes the original prudent investment of $179.95 seem like a very pleasant memory indeed! But the problems do not stop there… For example, after a while of the store being operational, the shop owners are bound to notice certain… trends. Like, why do people phone in and report that somewhere between clicking add to basket and going to checkout screen, their basket gets lost (something also reported on the forums). Intermittently. As you fix this sessions PHP error, you think… we’re ready for the big time, everything is sorted… Wrong again. CubeCart has more surprises in-store for you (great pun, if i say so myself). Over time, the speed of display of your product pages starts to increase. A lot…

Now, I use 24mb ADSL by be.there in London – pretty fast. I used to get the whole page (with all images) for something like 0.8 – 1.3 secs. Imagine my surprise when the delay between a link click and the start of page rendition (the ‘waiting for response’ phase) was over 4 seconds on its own! With nothing else changed to the best of my knowledge, my first reaction was to look at the MySQL database. I scanned it, profiled it, analysed it – to no avail. Deleted various search histories and session data entries, added some indexes… still nothing. Upgraded the MySQL version to 5.0.58 – no change (other than Fedora Core’s YUM removing Zend and MySQL support out of php.ini and Plesk refusing to work and pop3 passwords being rejected). Once I managed to fix all of that and turned by attentions back onto the website, the evidence still sides with my original suspicion: a database-related delay. It was time to do some query profiling… I found and modified the CubeCart DB class and added a timer and an echo wrapper around all mysql_query() calls. And there it was, waiting for me at the bottom of the page:

SELECT DISTINCT O.productId, I.name, I.image, I.price, I.sale_price FROM CubeCart_order_inv AS O, CubeCart_inventory AS I
WHERE I.productId = O.productId AND O.cart_order_id IN
(SELECT DISTINCT cart_order_id FROM CubeCart_order_inv WHERE productId = 274)
AND O.productId <> 274 LIMIT 3

3 rows in set (3.74 sec). Nice. Nicer still… site does not even use the ‘customers who bought this also bought’ feature! What’s happened? Well, over the last 6 months, the orders and orders inventory tables have grown to something over 2500 records – which has caused this nasty nested query that returns 3 measly product IDs (that may not even be relevant for an up-sell here) to bomb the server.

This begs the question: haven’t the CubeCart development crew done any testing on their system? Or hasn’t anyone that’s used CubeCart before gotten to 2000+ orders? Small wonder…

How many more red herrings are there waiting to be discovered in CubeCart, we’ll never know. Just a word of advice: do not pick this for your platform, even if given half a choice!


Oct 17th 2008 × The simple MODAL window via mootools

Having noticed a trend on Google Webmaster Tools and even Google Analytics for a fair amount of searches and results on the subject of ‘modal’, here is a little snippet to help.

There is only so much you can do to customise a simple layer that takes up the whole visible screen – z-index, colour and opacity. Another thing that can be useful is to pass on click events at creation. But you can build on it all of this as you see fit, here is the code itself:

var toggleModal = function(backgroundColour, options) {
    // modal view for the whole screen
    // ver 2.02 17/10/2008 03:42:06
    if ($("modal")) {
        $("modal").dispose();
        return false;
    }

    var options = $merge({
        zIndex: 10000000,
        opacity: .8,
        events: $empty()
    }, options);

    if (!$type(backgroundColour) && !$("modal"))
        return false;

    return new Element("div", {
        id: "modal",
        styles: {
            position: "absolute",
            top: 0,
            left: 0,
            width: window.getScrollWidth(),
            height: window.getScrollHeight(),
            background: backgroundColour,
            "z-index": options.zIndex
        },
        opacity: options.opacity,
        events: options.events
    }).inject(document.body);
} // end toggleModal

// example usage
toggleModal("#fff"); // create a white modal, default options.
// ... do stuff
toggleModal(); // close it.

// something more fancy...
toggleModal("#555", {
    zIndex: 1000,
    events: {
        click: function() {
            $("modal").fade(0); // want to gently fade it out
            // do other stuff/cleanup here
            (function() { toggleModal(); }).delay(500); // removes the layer itself after fade done
        }
    }
});

// the function also returns the object so you can tweak it from the outside:
toggleModal("#fff").adopt(new Element("div", {"class": "intro", text: "Hi from modal land!"}).addClass("modals");

I hope somebody finds this useful. Please be aware that IE applies transparency using a filter:opacity but has been known to have issues allocating memory for the filter. As a result if you modal a very long page (like this blog), transparency in IE7 may be lost.

Next snippet: coda bubbles / popups.


Oct 16th 2008 × mootools 1.2.1 released!

Just saw the news on the mootools mail list: 1.2.1 got released. It’s a minor bugfixing and performance-related update, with the most notable changes (for me anyway) the improved Safari 2 support:

[ADD] Element.set('html') now allows to set the innerHTML of all Elements (including tables and selects)
[ADD] Browser.Features.query to check if the Browser supports the new querySelector method on the document object
[ADD] Browser.Engine detection for WebKit version 525
[ADD] Browser.Engine detection for Opera 9.6
[ADD] Element.removeEvents now also accepts an object
[ADD] Class.removeEvents now also accepts an object
[ADD] Element.match now also accepts an Element
[CHG] Element.js has been refactored to make use of private variables wherever possible
[CHG] $unlink now returns an unlinked Hash instead of an object when a Hash is passed in
[CHG] Faster Element.hasChild
[CHG] The domready event for WebKit version 525 (or later) uses the native DomContentLoaded event
[FIX] Fixed getPosition in Internet Explorer to be faster and more reliable
[FIX] Selector [attribute!=val] now matches elements with empty attribute
[FIX] Element.clone is now much faster and retains state of form elements
[FIX] Fixed memory leaks related to IFrame unloading
[FIX] Fixed memory leaks related to Element storage
[FIX] Custom Events no longer stop the event when the condition returns false
[FIX] Documentation fixes and improvements
[FIX] :checked pseudo now works properly in Internet Explorer
[FIX] Class.js works in Safari 2 again, and contains no more eval hack
[FIX] Element text setter/getter is now working in Safari 2
[FIX] $exec is now working in Safari 2

Go and get it now


Oct 3rd 2008 × On javascript frameworks: unbiased throughts by two of the legends…

I have always dreamt about going to events like The Ajax Experience in Boston because I am a sad person; but also because you get to mee^H^H^Hsee people like John Resig of Mozilla (and er, author of a little thing called jQuery) and Aaron Newton, the CNET coder that brought mootools out of oblivion and mediocrity and continues to strive to take that great framework further (I own just the one hardcopy ‘pc-related/computer’ book and it happens to be MooTools Essentials by Aaron Newton – draw your own conclusions).

Anyway, as Aaron Newton once replied to a question of mine on the mootools google group, I guess I can now busk in his spotlight. So… A great and post cleverly and succinctly entitled jQuery, MooTools, the Ajax Experience, Programming to the Pattern, and What Really Makes One Framework Different From Another comes in the same breath as John Resig’s presentation on Javascript Libraries:

Nice read for a Friday night, huh? I live in a box… But you have to hate the duplicity and the political correctness here: say what you want but you are both champions to two of the biggest frameworks out there. It may be that mootools do not have aspirations for commercial success and recognition through the framework itself (yet), but they need to wake up and look at the big picture here: the more it is being used, the more popular it becomes, the more shared knowledge and improvements will come out of it through the incrased userbase. For Pete’s (who??) sake, jQuery is now in the Microsoft SDK and may even feature on Nokia’s phones… So, BE assertive. Don’t be afraid to point out exactly why framework xxx is better than framework yyy for achieving zzz. Because we (the users) don’t expect you to be nice to each other. After all, mootools is all about elitist attitudes, isn’t it? Lest we forget…

p.s. I blatently saw these two links on the ajaxian. So what?


Oct 3rd 2008 × A worthy PHP framework? Probably not but it is as close as anything else I have seen: codeIgniter

I have always regarded PHP frameworks that adhered to MVC standards and templating engines with inherent mistrust… No, that’s putting it too mildly. In fact, I really really really hated the idea. Let’s face it, somebody coming up with a legitimate way / reason to turn a simple page update on a site into a process that involves editing 4-5 files within the framework must have been paid by the hour… And the rest of the world must have been sleeping to accept this as the norm. I know in my production environment, where a quick reaction and turnaround is often critical, any delay is going to be considered a disaster (never-you-mind financial repercussions).

A typical example of a good idea gone horribly horribly wrong is eZ Publish – an award winning open source CMS system based on PHP, with it’s own framework and templating language. What-the-hell? Having actually worked in a company that dealt exclusively in producing eZ Publish websites and becoming a ‘certified eZ Now professional’, I can’t say I have ever seen anything so clumsy and awkward to develop for.
Hi, Tony Wood / VisionWT – I miss you guys!

Anyway, got sidetracked there… Was looking through some potential contract jobs today when I saw an ad for a PHP / AJAX coder using CodeIgniter. Since it pays 450 pounds a day, I actually made an effort and looked it up–and it turned out to be a WINNAR (anybody remember Jeff K?). I just finished watching the two excellent getting started video tutorials and it left me almost wanting to download CodeIgniter.

What set it apart? The ability to actually use inline PHP inside the view controllers and not have to learn some crappy inferior templating language. It means, if in a hurry, I can quickly code a hack and get it to work without too much messing around in a number of files (well, perhaps 2). Once happy with it and with the pressure off, I can move the change and optimise it in a ’semantic’ fashion to my heart’s contempt…

It deserves a mention and here is a plug of their own features:

  • You want a framework with a small footprint.

  • You need exceptional performance.
  • You need broad compatibility with standard hosting accounts that run a variety of PHP versions and configurations.
  • You want a framework that requires nearly zero configuration.
  • You want a framework that does not require you to use the command line.
  • You want a framework that does not require you to adhere to restrictive coding rules.
  • You are not interested in large-scale monolithic libraries like PEAR.
  • You do not want to be forced to learn a templating language (although a template parser is optionally available if you desire one).
  • You eschew complexity, favoring simple solutions.
  • You need clear, thorough documentation.

If even half that list is true, we still have a PHP framework that’s worth remembering – just on the off-chance you ever need one.

no