Showing posts with label javascript. Show all posts
Showing posts with label javascript. Show all posts

Tuesday, March 4, 2008

Html-R: Make our Ruby HTML Pretty

What?

Html-R, an ajaxified online Ruby2HTML converter.

Why?

When you share your code online, it should look good and be readable, just like in your favorite text editor (emacs, right?).

With TextMate inspired syntax highlighting, Html-R quickly turns this



# Why's (Poignant) Guide to Ruby - The Endertrombe Wishmaker
require 'endertromb'
class WishMaker
def initialize
@energy = rand( 6 )
end
def grant( wish )
if wish.length > 10 or wish.include? ' '
raise ArgumentError, "Bad wish."
end
if @energy.zero?
raise Exception, "No energy left."
end
@energy -= 1
Endertromb::make( wish )
end
end
into that

# Why's (Poignant) Guide to Ruby - The Endertrombe Wishmaker
require 'endertromb'
class WishMaker
def initialize
@energy = rand( 6 )
end
def grant( wish )
if wish.length > 10 or wish.include? ' '
raise ArgumentError, "Bad wish."
end
if @energy.zero?
raise Exception, "No energy left."
end
@energy -= 1
Endertromb::make( wish )
end
end

How?

Using jQuery, there's a listener that sits and waits for changes to the source code. After new code appears, it sends a re quest to the server which uses the syntax gem to generate the appropriate html.

The appended css is of my own design, and is intended to mimic that of TextMate.

Here are screenshots of the step by step process. Or you can just demo it yourself.




Friday, February 29, 2008

ExtJS & Rails - ExtJS scaffold generator installation script

What?
An automatic installation script for the ExtJS scaffold generator plugin (demo keynotes)to get you up and running.

Why?
Rails' scaffolding gets you up an running lightning fast. ExtJS is a wonderful javascript desktop library. Let's marry them

How?
Run the following script - it will

  • Create the rails application
  • Install the ExtJS scaffolding plugin
  • Download and install ExtJS for your application
  • Generate two sample models - posts and purchases
  • Setup the database
  • Create links to the sample models on the application landing page
  • Run the server
Then all you have to do is point your browser to http://localhost:3000, assuming the server is running on your computer.

Have fun!

# Create the app
rails ext_scaffold_setup
cd ext_scaffold_setup

# Install the plugin and extjs
script/plugin install http://rug-b.rubyforge.org/svn/ext_scaffold
curl -O http://extjs.com/deploy/ext-2.0.2.zip
unzip -q ext-2.0.2.zip
rm ./ext-2.0.2.zip
mv ./ext-2.0.2 ./public/ext

# Generate some scaffolds - posts and purchases
./script/generate ext_scaffold post title:string body:text
./script/generate ext_scaffold purchase order_id:integer amount:decimal description:text

# Create the database for the models
rake db:migrate

# Create links to the models on the landing page
echo '<title>ExtJS Scaffold generator sample</title>echo '<style type="text/css" media="screen">a {text-decoration:none; color:#abc}</style><a href="http://www.blogger.com/posts">Posts</a> <a href="http://www.blogger.com/purchases">Purchases</a>' > ./public/index.html

# All set, run the server!
./script/server

Point your browser to http://localhost:3000, and you're good to go.

Saturday, December 22, 2007

Javascript DOM Event Handlers - The Right Way

What?
In his YUI theatre presentation An Inconvenient API, Douglas Crockford gives a great rundown of browser history, inner workings, and shortcomings. Among other things he gave a simple, cross-compatible way of doing javascript event handling in the browser.

Why?
Event handling is an excellent pattern, attested by its standardization and widespread use. However, due to browser incompatibilities it is not always obvious how to do it in javascript.

How?
First off, there are three ways of assigning event listeners to nodes: the old way, the IE way, and the W3C way. The type variable should always be an event name, e.g. 'click', 'mouseover', etc. See PPK's thorough listing of event names and compatibility notes at Quirksmode.


// IE way - non-standard
function ieEvent(node, type, handler) {
node.attachEvent('on'+type, handler);
}

// W3C way - long name and with a third, useless parameter that *must* be specified (undefined is not good enough)
function w3cEvent(node, type, handler) {
node.addEventListener(type, handler, false);
}

// Classic way - works in all A-grade browsers
function classicEvent(node, type, handler) {
node['on'+type] = handler;
}


Since the classicEvent way works in all Yahoo classified A-grade browsers Douglas recommends to simply use that way. How we process an event is still different across browsers however - in W3C our event handler gets passed an event object, while in IE we have to get the global event object. We deal with these incompatibilities as follows:


// A Cross browser event handler
function normalizedEventHandler(fn) {
// Get the event object - passed as argument in W3C, global object in IE.
e = e || event;
// Figure out what element the event happened to - again, disparate naming.
var target = e.target || e.srcElement;
// Now do your thing:
// ...
}


We now put it all together in a fail-safe, hopefully forwards-compatible event handling function:


/**
* Cross browser event handling function.
* @param {Element} node The dom node to attach the event handler to
* @param {String} type The type of event, e.g. 'mousedown', 'mousemove', 'resize'
* @param {Function} handler The function to be called when the event happens. It gets passed an event object e, as well as a target node.
*/
function addEvent(node, type, handler) {
// Create our normalized event handler
var normalizedHandler = function(e) {
// Get the event object - passed as argument in W3C, global object in IE.
e = e || event;
// Get the element the event happened to
var target = e.target || e.srcElement;
// Now call the handler with the normalized event object
handler(e, target);
}
// Assign the event handler in a way our browser understands
if (node.addEventListener) {
node.addEventListener(type, normalizedHandler, false);
} else if (node.attachEvent) {
node.attachEvent('on'+type, normalizedHandler);
} else if (node['on'+type]) {
node['on'+type] = normalizedHandler;
}
}
Finally, as a side note: wtf is the third parameter in W3C's addEventListener method? It has with event capturing and bubbling to do. An event gets called on a node, then its parent, then its parent, and so on until the event is canceled or the root node is reached. This is called bubbling.

Netscape implemented the reverse, called trickling down, which visits the parent first, and then goes down until the target node. This turns out to be simply wrong. What did W3C decide to do? Both, of course. This is it: the third parameter says whether you should do capturing the right way (i.e. bubbling, by setting it to false) or the wrong way (i.e. trickling, by setting it to true).

Bottom line is, if you don't know what capturing and event bubbling is, you will want it to be false. Always. To read more on event capture and bubbling, see this article.

Quickly, to cancel a bubbling cross-browser:

// Don't bubble up the event, i.e. "The event has been handled" or "Don't tell my parents"
function cancelBubble(e) {
// IE way
e.cancelBubble = true;
// Everyone else's way
if (e.stopPropagation) {
e.stopPropagation();
}
}
Many thanks, as always, to Douglas Crockford for the excellent information.

Object Oriented Programming with Javascript

What?
As Joseph Smarr at Plaxo said in his YUI theatre talk, Javascript is a malleable language and can be bent to do what you want it to. However, the best thing to do is simply let it be what it wants to be.

Object Oriented Programming has proved an accurate pattern, as far as it makes us architect our abstract information structures in humanly comprehendible manners. As javascript has arguably become the most used programming language and declared by many as the next big Language it becomes increasingly important for us to learn how to use it properly (which many of us do not - as Douglas Crockford [more on him later] points out, it is also the worlds most misunderstood language)

Why?
Hacks serve a purpose - they solve problems without project overhead - but any code that is to be built upon and maintained must be understandable. It would seem that much of software's strength comes from its hierarchical structurability (The work you did yesterday I can use as a foundation for my work today), but this requires intuitive usability (APIs).

How?
In javascript, each class has a prototype-object that all other objects of the class refer to. When an object wants to use a method this.doSomething(), it calls the prototype.doSomething() function.

To make this work, we have to get around two novelties: classes are declared as functions, and the methods of the class are declared outside of the class body.

/**
* Our MissManners application
*/
var MissManners = {
nextUniqueId : 0
};

/**
* A miss manners guest
* @constructor
* @param {Object} params The parameters object. All other parameters are named properties of this object
* @param {String} name The guest name
* @param {String} gender The guest gender. 'male' or 'female'
* @param {String} interest The guest interest. A single string. You should give guests interests so that some match up - this is what the rule engine bases their placemenet on.
* @param {String} color Hexadecimal value of the color to represent the guest with.
*/
MissManners.Guest = function(params) {
this.name = params.name || this.Default.name;
this.gender = params.gender ? params.gender.toLowerCase() : this.Default.gender;
this.interest = params.interest ? params.interest.toLowerCase() : this.Default.interest;
this.color = params.color || this.Default.color;
this.id = 'MMGuest-' + MissManners.nextUniqueId++;
}

/**
* Generate and Html snippet to represent the guest.
*/
MissManners.Guest.prototype.toHtml = function() {
var result = '';
result += '<div class="missMannersGuest" id="'+this.color+'" style="background-color:'+this.color+'">';
result += '<ul>';
result += '<li>Name: '+this.name;
result += '<li>Gender: '+this.gender;
result += '<li>Interest: '+this.interest;
result += '</ul>';
result += '</div>';
}

/**
* The default values for a guest.
*/
MissManners.Guest.prototype.Default = {
name : 'Joan Doe',
gender : 'female',
interest : 'weather',
color : '#def'
}
We first initialize our MissManners object - this will contain our classes. Then we declare the Guest class, and at the same time define its initializer. This initializer defaults to return the this object.

Every function you create is given a prototype property, so that you can reference MissManners.Guest.prototype and build on it with the toHtml function.

We then go ahead and use our class:
function createGuest(divId) {
var guest = new MissManners.Guest({
name : 'Marcus Westin',
gender : 'Male',
interest : 'Flow',
color : '#cde'
});
document.getElementById(divId).innerHTML = guest.toHtml();
}
At this point, when we call guest.toHtml() it first checks to see if the newly created guest object has a function called toHtml(). If not, it then goes to its prototype, MissManners.Guest.prototype and checks if it has something called toHtml. If it does, it calls that function, and the this variable in toHtml gets bound to guest, such that this.name in toHtml is the same as guest.name in createGuest. If that prototype does not have the property or function, javascript goes to its prototype, and so on until it bottoms out in Object.prototype.

To view this code in action, as well as view the code in its entirety, see the demo. This entry builds on excerpt code of the Miss Manners demo app I wrote yesterday.

Wednesday, November 28, 2007

Drag n' Drop = Cut n' Paste

Here's a quick update on one of my current projects, the unnamed "online desktop" (name suggestions, anyone?).

Some new browser event handling hacks along with some css and innerHTML fiddling allows you to drag and drop a video from your resources into your text editor.



The visual cues are still rough, but the concept is there - "principle at work."

Release is due in late December. Hit me up if you want a trail at that time.

Monday, November 26, 2007

Named Parameters in Javascript Functions

Named Parameters with {}
Named parameters is a blessing. When you write Java, eclipse helps you to remember parameter order with auto-completes. But wouldn't it be nice to remember arguments by name rather than by index?


function friendToHtml(friend) {
html = "<span>"
html += "<h1>" + (friend.name || "Joe Schmoe") + "</h1>"
html += "<ul>"
html += "<li>Phone " + (friend.phone || "<i>Not listed</i>") + "</li>"
html += "<li>Email " + (friend.email || "<i>Not listed</i>") + "</li>"
html += "<li>Address " + (friend.address || "<i>Not listed</i>") + "</li>"
html += "</ul></span>"
return html;
}


friendToHtml({
name : 'Marcus',
address : '5500 S Shore Drive, Chicago IL',
email : 'narcvs@gmail.com',
phone : '1-800-javascript'
}) // -->
// <span><h1>Marcus</h1><ul>
// <li>Phone 1-800-javascript</li>
// <li>Email narcvs@gmail.com</li>
// <li>Address 5500 S Shore Drive, Chicago IL</li>
// </ul></span>


friendToHtml({
email : 'callMe@example.com'
}) // -->
// <span><h1>Joe Schmoe</h1><ul>
// <li>Phone <i>Not listed</i></li>
// <li>Email callMe@example.com</li>
// <li>Address <i>Not listed</i>
// </li></ul></span>


The Default Operator ||
Note that we did not have to specify the parameters in the correct order. Another advantage is that named parameters also allow for painless default value assignment. In fact, the || operator is also called the "default operator." For example, if name was not specified, just fall back to "Joe Schmoe."

...

html += "<h1>" + (friend.name || "Joe Schmoe") + "</h1>"

...


The Guard Check a ? b : c
Sometimes you might not be 100% sure that an object passed to your function was actually defined. Let's say that your friends sometimes have a list of favorite books (but not always), and that you want to include the first book on the list in the html description.

If the friend.books field is not specified and you try to access friend.books[0] an exception will be thrown. In order to avoid this you would usually write:

...

if (friend.books) {
html += "<li>Favorite book " + friend.books[0] + "</li>"
} else {
html += "<li>Favorite book <i>Not listed</i></li>")
}

...

But that's just ugly. Here is a one liner that checks that books have been defined, and otherwise defaults to another value

...

html += "<li>Favorite book " + ((friend.books && friend.books[0]) || "<i>Not listed</i>") + "</li>"

...
This way you first check that friend.books is defined. If it is not then default to Not listed

Sunday, May 13, 2007

Blink blink

Finally the new Web 2.0 revolution has settled the standard-compliancy issue of the HTML <blink> tag! All you have to do is <span> your <blink> text and add a bit of <script>:

function makeXMLRequest() {
var browser = navigator.appName;
if (browser == "Microsoft Internet Explorer") {
return new ActiveXObject("Microsoft.XMLHTTP");
} else {
return new XMLHttpRequest();
}
}

var request = makeXMLRequest();

function sendXMLRequest(url) {
request.open('get', url);
request.onreadystatechange = handleResponse;
request.send(null);
}

function handleResponse() {
if (request.readyState == 4) {
var response = request.responseText;
document.getElementById('insert').innerHTML = response;
}
}
Then complete it all with an
<body onload="javascript:sendXMLRequest('blink-on.html')">
to you back-end server blinker applet, and you've got your blinking text!