Introducing JSAmp – An unobtrusive Javascript solution for sexy ampersands

The idea for this little script was born while chatting with Medo, a year or two back in London. The bunch of us were in London for that year’s FOWA event. Anyways, the code has been lying around for way too long — I finally found the time to finish it up and release it.

What does it do?

Well, not much. It’s an unobtrusive Javascript implementation of search & replace basically. It replaces ampersands (in their various possible forms) inside certain (configurable) html elements with something along the lines of <span class="amp">&amp;</span> (also configurable).

This replacement, however, enables you to do some sexy styling of ampersands inside your headings for example. It can get really freaky, and really, really, really nice visually.

People usually do this on the server-side (and there are many, many server-side flavored plugins/functions/solutions out there), but my take on this is client-side. Anyone should be able to use it. (well, almost anyone).

Check out the demos and the configurator.

The code is very simple, documented, MIT licensed and comes in two flavors:

  • The full source (5KB)
  • Packed version (820 bytes)
    [don’t get this one. modify the full source and pack it yourself with your modifications. This is used just as an example of the file size reduction gained with packing]

Have fun, and drop me a line if you have something to add.

P.S.
The lack of updates on the blog is noticeable. Sorry about that, it’s been hectic around here. Hoping it would normalize soon, so I can put the finishing touches on the rest of my drafts…

I r naked. For it’s css naked day!

http://naked.dustindiaz.com/

New books arrived — Amazon, where are the bookmarks?

The books are finally here! Time for some heavy reading.

One thing strikes me as odd though — I don’t remember ever receiving a bookmark from Amazon. You know, the simple piece of paper you place in a book, marking a point to which you’ll return. A placeholder of sorts. And I’ve been buying books from them for the past several years.

Last night I started reading one of the new books, when a sudden urge to hit the crapper emerged. Not wanting to take the book with me and in dire need of a bookmark, I did a quick Ctrl + f around the living room. Results: 4 brand new Amazon books and not a single damn bookmark — Amazon, WTF?

I find this rather peculiar, since the bookmark presents a wonderful opportunity to do something extra for your customers, at practically no cost. Spend some extra time designing it and you can have a very useful and appreciated extension of your online presence. Don’t do it and I’ll hate you for the evening. And post about it the following morning.

What’s your opinion? Have you ever recieved a free bookmark with your order? Do you have more information as to why they don’t do this already?

Asus EEE Pc — so much fun

Got my baby this morning. Pearl colored, couldn’t wait till the black one becomes available in Croatia, God only knows when that’ll happen.

Once they become available in 8GB and with more RAM, I’m getting the black one, and surrendering this baby over to wife-to-be.

Untill then, I’ll be having fun compiling stuff over and over again, checking dependencies, RTFMing my ass off and generally having so much fun. This thingy is made to be tweaked to hell and back again. BTW, it fully boots in under 10 seconds. For now at least : )

So far, I’ve managed to:

  • Remember that the console is brought up with Ctrl + Alt + T. I was lost without it for the first 20 mins. Then I changed the default console to Konsole following a guide on the wiki. Thinking of trying another one since Konsole seems slowish to start. Restart X server with Ctrl + Alt + Backspace
  • Learn the hard way about Alt + click anywhere inside a window = drag it from that point onward. Using apps that weren’t designed for an 800×480 screen is painful. Very.
  • Get my cell phones hooked up via some crappy bluetooth dongle and actually get them to see the eeepc and vice versa. Next step was to get Dial-up networking working, so now I should be able to go online via the phone’s GPRS.
  • Build synergy and quicksynergy from sources and get them both working. Also a major PITA: first, stock eeepc doesn’t come with any sort of compiler. Or a good software repository for that matter. So I had to get those working first. Since I haven’t had much experience with Debian before, I learned a lot along the way and found out that I’ve wasted about 2 hours manually looking for stuff, when I could’ve just typed: sudo apt-get install build-essentials and magic happens and you get all that’s needed to compile stuff from sources. Yay!
  • Write my first blog post using this tiny little keyboard!

Pics and some other goodies coming soon hopefully. Need more time and less distractions from the real world dammit!

EDIT: Bleh, now I see that my blog looks wierd in Firefox 2.0.0.9 and 800×480 screen. Gonna try fixing that tomorrow.

Seth Godin’s Meatball Sundae

As I dragged my sleepy ass to the office this morning, Maratz was already there with a surprise! His latest Amazon package arrived. Among the stuff he ordered was a copy of Seth Godin’s Meatball Sundae that I mentioned to him in an e-mail just a few days ago. Woot!

Seth Godin's Meatball Sundae book on my desk at work. Taken with a crappy mobile phone camera

I really think this book is a must read for anyone doing anything even remotely related to the Web and it’s marketing aspects (of which there are many). Seth’s ability to grasp concepts and present them to a broad audience — using simple examples that many people can relate to — is remarkable.

The first thing I noticed though, after mentioning it to several colleagues in the building (praising the book’s contents) is rather troublesome — 2 out of 3 people had problems just making out the title! Seems that both words are unknown to them, not to mention the suffering they had to go through whilst trying to spell the damn thing out loud.

Creative “marketing” on Godin’s part, or just absence of a little knowledge about American culture on their part? One wonders…

Anyways, mention the book to your boss — even better, make him/her read it!

Make your search boxes (inputs) smarter with Javascript: Search_Box behavior

Almost every new web site has (or should have) a search box. User skills are improving and people know the basics of using search and use it more often then before. So, your site has a search box (or it’s gonna have one pretty soon).

Search boxes are useful (but boring) by default

The search box is much nicer and much more useful if it has a default value that can act as many different things: a call to action, an explanation, a teaser of any kind, or just as an outlet for your witty messages. However, once you’ve set a default value, your users are forced into clearing the value manually before actually being able to use the thing. Putting extra work on our users is always a no-no, and as an added bonus we get to have some Javascript fun!

OK, but why?

Because it’s fun! And because I’ve implemented my fair share of those damn search boxes, and implementing the same thing over and over again made me feel stupid. I got tired of it and whipped this thing up so I don’t have to ever think about the issue: I just include the script where it’s needed and voilà!

Another thing that I find annoying is that by default browsers do not select the field contents once the field is focused (in forms, but they do select the contents if you focus a location bar for example) — at least on Windows — so I decided to fix that too.

Onfocus, onblur – say what?

Using onfocus and onblur event handlers we can add behavior to our search box. Here’s the logic: if the search box receives focus, we’re gonna clear the value of the box (only if the current value is the same as default, or if it’s blank), and when the input looses focus (onblur) we’re gonna set the default value back (only if the user hasn’t changed the value). It might sound confusing, but you can check the search box in the sidebar to see what I’m talking about.

The examples

Some obligatory examples:

The script

The fully commented source is listed below, and a minified version is also available. It’s free to use however you see fit (the MIT License).

It has been tested and confirmed working on all of the following:

  • Windows XP / Firefox 2.0.0.11
  • Windows XP / Safari 3.0.4 (beta)
  • Windows XP / Opera 9.25
  • Windows XP / IE 6.0
  • Windows Vista / IE 7.0
  • Mac OS X 10.5 Leopard / Safari 3.0.4
  • Mac OS X 10.5 Leopard / Firefox 2.0.0.11

If anyone has a chance to test on other platforms/browsers and finds a bug or quirk or something, let me know.

Known issues:

  • Selecting focused field’s contents using select() is acting strangely in Safari (across all the platforms) — looking into it
/**
* Search_Box behavior
* @author   zytzagoo
* @version  0.2
*/
function Search_Box(cfg) {
    // defaults are always nice
    var defaults = { ELEMENT_ID: 'q', DEFAULT_VALUE: 'inherit', FOCUSED_VALUE: '' };
    if (cfg) {
        // we have a cfg, loop thru the properties
        // and make sure something is not missing
        // if so, add it from the defaults
        for (var name in cfg) {
            if (cfg.hasOwnProperty(name)) {
                for (var defname in defaults) {
                    if (defaults.hasOwnProperty(defname)) {
                        if (!(cfg[defname])) {
                            cfg[defname] = defaults[defname];
                        }
                    }
                }
            }
        }
    } else {
        cfg = defaults;
    }
    /**
    * return a new object literal with extra
    * stuff attached on it
    */
    return {
        /**
        * Checks the element we're working on exists, and
        * attaches handlers to it. Usually called after the document
        * has loaded or (even better but harder to achieve truly
        * cross-browser) when the element referenced by
        * Search_Input.ELEMENT_ID is available in the DOM.
        */
        init: function () {
            var el = document.getElementById(cfg.ELEMENT_ID);
            if (el) {
                /**
                * special case: 'inherit'
                * This resets the passed in default value and
                * if the element has a previously set value, that
                * value is used as the default from now on.
                */
                if (cfg.DEFAULT_VALUE === 'inherit') {
                    cfg.DEFAULT_VALUE = '';
                    if (el.value !== '') {
                        cfg.DEFAULT_VALUE = el.value;
                    }
                }
                /**
                * If a default value is specified, override
                * whatever exists in the value attribute of the input in html
                */
 
                /**
                * if we have a custom focus handler passed in,
                * attach that one too and make sure it is called first
                */
                if (cfg.focus) {
                    Search_Box.attach_handler(el, 'onfocus', cfg.focus);
                }
                // our own focus handler is always attached
                Search_Box.attach_handler(el, 'onfocus', this.focus);
                /**
                * same as above except this takes care of onblur handlers
                */
                if (cfg.blur) {
                    Search_Box.attach_handler(el, 'onblur', cfg.blur);
                }
                // our own onblur handler is also always attached
                Search_Box.attach_handler(el, 'onblur', this.blur);
                /**
                * in case the elem has no current value,
                * set it to the specified default
                */
                if (el.value === '' || (cfg.DEFAULT_VALUE &amp;&amp; cfg.DEFAULT_VALUE !== '')) {
                    el.value = cfg.DEFAULT_VALUE;
                }
            } else {
                throw new Error('Search_Box.init: element (id: "' + cfg.ELEMENT_ID + '") doesn\'t exist');
            }
        },
        /**
        * Handles the onfocus event of the element
        */
        focus: function (e) {
            // delegate, passing in the event object
            var t = Search_Box.get_target(e);
            // if the target of the event is an input element
            if (t.nodeName.toLowerCase() === 'input') {
                // if the value of that input is empty or default
                if (t.value === cfg.DEFAULT_VALUE || t.value === '') {
                    // set the value to the specified focused value
                    t.value = cfg.FOCUSED_VALUE;
                    /**
                    * if the now set focused value is not empty
                    * select the contents of the box
                    */
                    if (t.value !== '') {
                        t.select();
                    }
                }
            }
            return true;
        },
        /**
        * Handles the onblur event of the element
        */
        blur: function (e) {
            // delegate, passing in the event object!
            var t = Search_Box.get_target(e);
            // if the target of the event is an input element
            if (t.nodeName.toLowerCase() === 'input') {
                /**
                * if the current value of that element is the
                * focused value or empty, set the current
                * value to the specified default value
                */
                if (t.value === cfg.FOCUSED_VALUE || t.value === '') {
                    t.value = cfg.DEFAULT_VALUE;
                }
            }
            return true;
        }
    };
}
/**
* Gets the target of an event
*/
Search_Box.get_target = function (x) {
    x = x || window.event;
    return x.target || x.srcElement;
};
/**
* Attaches event handlers to an existing object.
* If an existing handler is found, it is executed before
* our newly attached handler
*/
Search_Box.attach_handler = function (o, evt, f) {
    if (o !== null) {
        var existing_handler = o[evt];
        if (typeof o[evt] !== 'function') {
            /**
            * no previous handler found
            * TODO: this might need looking into,
            * but it seems to work so far...
            */
            o[evt] = f;
        } else {
            /**
            * Previous handler found, invoke it,
            * while making sure that the 'this' keyword
            * inside the handler function refers to the
            * input element.
            * This enables some cool custom onfocus and
            * onblur handlers possible and logical and
            * easy to develop and not worry about naming
            * stuff...
            */
            o[evt] = function (e) {
                existing_handler.apply(o, arguments);
                f.apply(o, arguments);
            };
        }
    }
};

My Amazon.com order has shipped!

Weeee! Just got a nice e-mail from Amazon:

The following items have been shipped to you by Amazon.com:

  • Beautiful Code: Leading Programmers Explain How They Think (Theory in Practice (O’Reilly))
  • RESTful Web Services
  • Programming Collective Intelligence: Building Smart Web 2.0 Applications

Expect short reviews as soon as I’m finished with them.

On a side note, I’ve recently finished reading the massive “Javascript: The Definitive Guide” (O’Reilly) and can really recommend it if you plan on venturing into the wonderful world of Javascript.

The “Programming Erlang: Software for a Concurrent World” hasn’t shipped yet dammit :(

Updated wordpress to 2.3.3

Hello broken search box!

Actually, it was broken only for a few minutes, since I was clever enough to make a diff of changes.
All is good. Carry on.

P.S.
A simple self-contained totally unobtrusive Javascript snippet for manipulating (simple) text input’s focus and blur behaviors incoming soon…

WTF is it with the whole “quit smoking” thing lately?

Anyone else noticing the high influx of “quit smoking”, “stop smoking”, “you can do it”, “X patch”, “Y patch” type of campaigns recently? Like, the moment the word got out that new legislation is about to be pushed here in Croatia (banning smoking in public places), they started rampaging. It’s like El Dorado. The ad agency owners feel all warm and fuzzy. From what I hear it’s almost as good as the old days: The Golden Age of Croatian Telecom Monopoly (HT, the one and only) Days.

Outdoor advertising, tv commercials, print commercials, promotional websites, radio shows, “experts” having tv and radio guest appearances in anything even remotely connected to “lifestyle” (quoted for general hate towards anything with that fancy-sounding-meaning-nothing name) or health… New brands keep showing up on a daily basis. It just doesn’t stop.

Now, if you’ve ever been involved in the production part of any of the above-listed things, you know that it takes a certain amount of time to actually produce them. Even if you’re just supposed to translate crap given to you by some franchise office. And yet, the big companies launched their campaigns the following morning.

I’ll leave the simple addition of 2 + 2 as an exercise for the reader.

Croportal’s voting widget pushed live

Just a side note about a feature I’ve been working on lately, that’s now officially released live: Croportal dev blog: Integracija linka za glasovanje (hr)

Because the announcement is in Croatian, here’s a quick recap:

The feature enables site owners to integrate voting buttons for their content directly on their sites (something along the lines of reddit, digg etc).
The buttons are included with inserting a simple <script src="..."> one-liner anywhere on a page. Our API takes over from there.

The system figures out if the content already exists on our end and if it does displays real-time stats for it. If the content doesn’t exist yet, the system pushes out a link that makes submitting that content practically a one-or-two-click operation.

If the user browsing your site is also logged on to our site (via “remember me” functionality or just for the current browsing session) he can vote with a one-click action without ever leaving your site. The click fires up an async AJAX call and registers the user’s vote.

We’re currently offering some customization options (for pre-populating certain submission related variables which make content submission even easier for your visitors) and we’ve bundled two skins: a horizontal and a vertical one (so everyone should be able to integrate the buttons easily without them having an impact on their site’s layout).

Further customization and layout options are a piece of cake to implement now, but first let’s see what the users think and what their usage patterns are.

This is just the first of (I hope) many new exciting features which will all use Croportal’s API. My plan is to make practically every (common sense) piece of content available through the public API. When that’s done, sky’s the limit in terms of possible mash-ups, widgets, data visualizations, integrations etc.

Fun times ahead.