925 HTML http://925html.com For those of us who work with the web daily. Tue, 04 Jan 2011 07:09:28 +0000 en hourly 1 http://wordpress.org/?v=3.0.4 Photos Around You http://925html.com/code/photos-around-you/ http://925html.com/code/photos-around-you/#comments Mon, 05 Apr 2010 14:00:01 +0000 Eric Ferraiuolo http://925html.com/?p=527 On the tails of the exciting release of YUI 3.1, I wanted to build a little mini web-app to show some of the powerful new features. I built Photos Around You, an app which determines your Geolocation and finds photos geo-tagged around this location. Building this app took ~200 lines of YUI 3.1 JavaScript that I had to write; and this is how its put together:

A screenshot of the Photos Around You Web-App

YUI 3.1 Modules Used

  • node: The Node Utility provides an expressive way to collect, create, and manipulate DOM nodes.
  • overlay: Overlay is a positionable and stackable widget, which also provides support for the standard module format layout, with a header, body and footer section.
  • substitute: Does variable substitution on a string. It scans through the string looking for expressions enclosed in { } braces. If an expression is found, it is used a key on the object.
  • gallery-jsonp: Provides a JSONPRequest class for repeated JSONP calls, and a convenience method Y.jsonp(url, callback) to instantiate and send a JSONP request.
  • gallery-yql: This module adds a little sugar to YUI3 to make simple easy YQL queries.
  • gallery-markout: Markout is an API for creating DOM nodes in JavaScript. Much easier to use than the built-in DOM API, and allows for easy delegation for code modularity.

YUI 3.1’s loading and dependency management features make this extremely easy even for YUI 3 Gallery Modules which are contributed from the YUI community.

<script src="http://yui.yahooapis.com/3.1.0/build/yui/yui-min.js"></script>
<script>
	YUI().use('node', 'overlay', 'substitute', 'gallery-jsonp', 'gallery-yql', 'gallery-markout', function(Y){
		// Everything is ready!
	});
</script>

Determining the User’s Location

Christian Heilmann (codepo8) published [on Github] an ingenious way to determine the user’s location using YQL and the W3C Geolocation API with IP-based location identification as a fallback. Basing my code on Heilmann’s work and using YUI 3.1 I wrote a set of functions to get the user’s Geolocation.

Getting The User’s IP

Using the JSONP YUI 3 Gallery Module written by Luke Smith (YUI Core Team) and the JSONIP Google App Engine app getting the user’s IP is simple:

getIP = function (callback) {
	Y.jsonp('http://jsonip.appspot.com/', function(data){
		callback(data.ip);
	});
};
Getting Location Data From An IP

We have the user’s IP, but we need info about the physical location of that IP address; we can get this info using YQL tables: ip.location, flickr.places, and geo.places.

locFromIP = function (ip, callback) {
	var query = 'select * from geo.places where woeid in ' +
				'(select place.woeid from flickr.places where (lat, lon) in ' +
				'(select Latitude, Longitude from ip.location where ip="{ip}"));',
		yql;

	yql = new Y.yql(Y.substitute(query, { ip: ip }), function(r){
		callback(r.query && r.query.results ? r.query.results.place : null);
	});
};
Getting Location Data From Geolocation Position

Almost identical to the YQL query to get the location data from an IP address, but where we already have the latitude/longitude coordinates from the W3C Geolocation API.

locFromPos = function (pos, callback) {
	var position = { lat: pos.coords.latitude, lon: pos.coords.longitude },
		query = 'select * from geo.places where woeid in ' +
				'(select place.woeid from flickr.places where lat={lat} and lon={lon});',
		yql;

	yql = new Y.yql(Y.substitute(query, position), function(r){
		callback(r.query && r.query.results ? r.query.results.place : null);
	});
};
Feature Detect W3C Geolocation API, Fallback to IP

A simple check for the presence of navigator.geolocation allows the use of the Geolocation API to fetch the user’s current position if granted (the user is prompted by the browser to authorizing giving the application the location.); otherwise we fallback to using the IP-based location.

if (navigator.geolocation) {
	navigator.geolocation.getCurrentPosition(
		Y.rbind(locFromPos, this, callback),
		Y.bind(getIP, this, Y.rbind(locFromIP, this, callback))
	);
} else {
	getIP(Y.rbind(locFromIP, this, callback));
}

Finding Geo-tagged Photos

Flickr, with its extensive API, intrinsic YQL support, and mass of awesome photos seemed the logical place to fetch the photos for this app. Equipped with a WOEID (Where On Earth IDentifier) corresponding to the user’s location, we can initiate a Flickr Photos Search API request using the flickr.photos.search YQL table.

getPhotos = (function(){

	var query = 'select * from flickr.photos.search({start},{num}) ' +
				'where woe_id="{woeid}" and radius_units="mi" and sort="interestingness-desc" and extras="path_alias";';

	return (function (loc, start, num, callback) {
		var yql = new Y.yql(Y.substitute(query, { woeid: loc.woeid, start: start, num: num }), function(r){
			callback(r.query && r.query.results? r.query.results.photo : null);
		});
	});
}());

Photo Utility Functions

I wrote a few utility functions to encapsulate the common tasks this app is doing with photo data.

getPhotoURL(photo, size)

Using the Flickr URL patterns we can generate a URL to an image file on Flickr of a particular size.

getPhotoURL = (function(){

	var template = 'http://farm{farm}.static.flickr.com/{server}/{id}_{secret}{size}.jpg';

	return (function (photo, size) {
		return Y.substitute(template, Y.merge(photo, { size: size ? '_'+size : '' }));
	});
}());
getPhotoPageURL(photo)

I wanted to create links back to the Flickr Photo Pages when viewing the medium-sized photo in the overlay; this function generates that URL.

getPhotoPageURL = (function(){

	var template = 'http://www.flickr.com/photos/{user}/{id}';

	return (function (photo) {
		return Y.substitute(template, { id: photo.id, user: photo.pathalias || photo.owner });
	});
}());
loadImg(src, callback)

A utility function derived from Luke Smith’s work to determine when an image has fully loaded. Using this approach and waiting until all the image’s bytes have been loaded became crucial to aide the sizing and positioning of the Photo Overlay.

loadImg = function (src, callback) {

	// insired by: Lucas Smith (http://lucassmith.name/2008/11/is-my-image-loaded.html)

	var img = new Image(),
		prop = img.naturalWidth ? 'naturalWidth' : 'width';

	img.src = src;

	if (img.complete) {
		callback(img[prop] ? img : null);
	} else {
		img.onload = Y.bind(callback, this, img);
		img.onerror = Y.bind(callback, this, null);
	}
};
renderThumbnail(photo)

A simple function using MarkoutJS to render a photo thumbnail into a list-item node.

renderThumbnail = function (photo) {

	var li = Y.Markout().li({ 'class': 'photo' });
	li.img({ src: getPhotoURL(photo, 's'), title: photo.title });
	return li.getNode();
};
renderPhoto(photo, size)

Another utility function using MarkoutJS to render a larger version of the photo with it’s title into a DocumentFragment. This is used to set the contents of the Overlay which appears when you click on a photo thumbnail.

renderPhoto = function (photo, size) {

	var df = Y.Markout();
	df.div().a({ title: 'View on Flickr', href: getPhotoPageURL(photo) }).img({ src: getPhotoURL(photo, size), alt: photo.title });
	df.p().text(photo.title);
	return df.getNode();
};

Putting It All Together

At this point I have a way to get the user’s location, fetch photos from Flickr based on a location, and photo utility functions; this is how I put it all together:

Constructing The Photo Overlay

YUI 3.1 comes with a feature-rich Overlay Widget; for this app I’m creating a single Overlay instance which a larger version of the photo will be rendered into when the user clicks on a photo thumbnail.

photoOverlay = new Y.Overlay({
	visible         : false,
	centered        : true,
	width           : '550px',
	zIndex          : 100,
	constrain       : true,
	headerContent   : '<span class="close">×</span>',
	render          : true
});

I’m keeping the Overlay hidden as first, and rendering a close “button” in the header area.

Clicking a Thumbnail Shows Larger Version in the Overlay

Using YUI 3.1’s Event Delegation, I attached a click handler to the list of thumbnails which is only triggered if a thumbnail is clicked. Using the photo’s data (which is stored with the thumbnail Y.Node instance) I’m setting the content of the Overlay to the larger version of the photo and making sure the Overlay is visible.

Y.delegate('click', function(e){

	var thumbnail = e.currentTarget.addClass('loading'),
		photoData = thumbnail.getData();

	loadImg(getPhotoURL(photoData), function(){
		photoOverlay.setStdModContent(Y.WidgetStdMod.BODY, renderPhoto(photoData));
		photoOverlay.show().centered();
		thumbnail.removeClass('loading');
	});

}, '#photos', '.photo');
The Overlay’s Close “button”

The only fancy part here is the CSS used to style the close “button”; the JavaScript is pretty straight forward:

Y.one('.close').on('click', Y.bind(photoOverlay.hide, photoOverlay));
.yui3-overlay-content .close {
	position: absolute;
	top: -10px;
	left: -10px;
	-webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 0.50);
	-moz-box-shadow: 0 0 4px rgba(0, 0, 0, 0.50);
	box-shadow: 0 0 4px rgba(0, 0, 0, 0.50);
	border: #f9f9f9 2px solid;
	-webkit-border-radius: 10px;
	-moz-border-radius: 10px;
	border-radius: 10px;
	background: rgb(34, 34, 34);
	width: 18px;
	height: 18px;
	line-height: 16px;
	text-align: center;
	font-size: 1.143em;
	font-weight: 700;
	cursor: pointer;
}
Wiring Everything Together

Now for the main “run-time” of the app. This is the code that makes the calls to get the user’s location, fetch the initial set of 100 photos based on the location, renders them as thumbnails, and builds and wires-up a “more photos” button.

getLocation(function(loc){

	var locality = loc ? loc.locality1.content : null,
		num = 100,
		start = 1,
		heading = Y.one('h1'),
		loading = Y.one('#loading');

	if ( ! loc) {
		loading.remove();
		heading.set('text', 'No Photos Around You :-(');
		return;
	}

	loading.set('text', 'Fetching ' + locality + ' Photos…');
	heading.set('text', 'Photos Around ' + locality);

	getPhotos(loc, start, num, function(photos){

		var photosNode = Y.one('#photos'),
			df = Y.Markout().getNode(),
			more;

		loading.remove();

		if ( ! photos) {
			heading.set('text', 'No Photos Around ' + locality + ' :-(');
			return;
		}

		photos = Y.Lang.isArray(photos) ? photos : photos ? [photos] : [];
		Y.Array.each(photos, function(photo){
			df.append(renderThumbnail(photo).setData(photo));
		});
		photosNode.append(df);

		if (photos.length === num) {
			more = Y.Markout('#content').p({ id: 'more' }).button().getNode().set('text', 'More ' + locality + ' Photos');
			more.on('click', function(){

				more.set('disabled', true).set('text', 'Loading More ' + locality + ' Photos…');

				getPhotos(loc, start+=num, num, function(photos){

					var df = Y.Markout().getNode();

					photos = Y.Lang.isArray(photos) ? photos : photos ? [photos] : [];
					Y.Array.each(photos, function(photo){
						df.append(renderThumbnail(photo).setData(photo));
					});
					photosNode.append(df);

					more.set('disabled', photos.length < num).set('text', 'More ' + locality + ' Photos');

				});

			});
		}

	});

});

View Photos Around You

]]>
http://925html.com/code/photos-around-you/feed/ 9
Auto-Building YUI 3 Custom Modules http://925html.com/techniques/auto-building-yui-3-custom-modules/ http://925html.com/techniques/auto-building-yui-3-custom-modules/#comments Tue, 26 Jan 2010 05:21:13 +0000 Eric Ferraiuolo http://925html.com/?p=469 The details of my development strategy to automatically build YUI 3 Custom Modules that I outlined in my talk at YUICONF 2009.

YUI 3’s modularity and instance sandboxing invites developers to create their own custom modules, breaking apart their specific application logic.

Diagram depicting the categories of YUI 3 modules on a scale from general to specific

YUI 3 Modules Categories from General to Specific

Having developed over 30 Custom YUI 3 Modules in the last year I quickly realized I needed a sane way to go from writing code to running it in the browser. So, I set out to connect the YUI Builder with my IDE.

For the last three or more years I’ve been using Aptana Studio IDE for HTML, CSS, and JavaScript code development. Aptanta, which is built on Eclipse, can be used standalone or as a plugin to Eclipse. The concepts here are not specific to Eclipse; the details of my technique on implementing the concepts are however.

Goals and Reasons

  • Synchronize UI building with the rest of the project code.
    Compiling of JavaScript (when needed) should occur along with any server-side code compilation. IDEs such as Eclipse, ‘Build Automatically’ a project’s Java code; I want my YUI 3 Custom Modules to ‘Build Automatically’ as well.
  • Faster turn-around.
    I don’t want to switch from my IDE to another program to run builds on my UI code. The IDE should recognize when it needs to re-build my UI code. I want a process where I: hack… hack… save… refresh-browser…
  • Easier for non-UI developers.
    Teams of developers are usually working on a shared code-base; a mixture of server-side and client-side code wrapped up into one project. If the UI code is built automatically, like the server-side code, the non-UI developers won’t bitch don’t have to do anything UI-related.
  • Cmd + S = JSLint, Compress, & Deploy.
    Anytime the source file(s) to a YUI 3 Custom Module change, the YUI Builder tool runs, giving me: JSLint-ing, YUI Compressor-ing, and YUI 3 Module wrapping.

Auto-Building in Eclipse IDE

Eclipse is an open, extensible, integrated development environment. Eclipse’s External Tools feature a great place to integrate the YUI Builder into the IDE:

“External tools allow you to configure and run programs, batch files, Ant buildfiles, and others using the Workbench. You can save these external tool configurations and run them at a later time.

Output from external tools is displayed in the console view.

You can add external tools as part of the build process for a project. These external tools will run in the specified order every time a project is built.” — Eclipse.org

Eclipse External Tools Builders can run Ant tasks, the YUI Builder and YUI 3 Custom Module build files are Ant tasks, perfect!

The goals listed above are reachable. I’m, not going to lie, this was hard to get working the first time; I feel it’s best to go through the process in steps.

0. Git YUI Builder

You need a copy of the YUI Builder project. If you already have Git clones of other YUI projects, make the Builder project a sibling to them.

$ git clone git://github.com/yui/builder.git

Detailed YUI Builder installation instructions.

1. Set builddir as a Global Ant Property in Eclipse

YUI 3 Module build files need to be configured with a builddir property; Eclipse can be configured with the path to YUI Builder on your system. By creating this property you will not have to hard-code the path to the YUI Builder project; making it easier to share code amongst a team. Everyone in your team will need to set the builddir Ant property in their Eclipse configuration; a trade-off between configuration and development environment convention.

Eclipse > Preferences > Ant > Runtime > Properties
Name: builddir
Value: /path/to/yui/builder/componentbuild
The value will needs to be set for each system specifically.

Screenshot of Eclipse’s Ant Global Properties configuration

Eclipse’s Ant Global Properties

2. Create YUI 3 Module Directory Structure

For each YUI 3 Custom Module you create, you’ll want to create a directory structure that mimics the default structure for YUI 3 Core Modules (Note: The directory structure can be different, you’ll just have to “tell” the YUI Builder via extra configuration in the build.properties file):

Assume we’re creating a module named test

/../
	test/
		js/
			test.js
		build.xml
		build.properties

3. Create build.xml and build.properties Files

The build.xml file is ran by Ant, it defines some meta-data about the module build and imports the module-specific build.properties file.

Again, assuming a module named test

build.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- Test Component Build File -->
<project name="Test" default="local">
    <description>Test Build File</description>
    <property file="build.properties" />
    <import file="${builddir}/3.x/bootstrap.xml" description="Default Build Properties and Targets" />
</project>
build.properties

I left in the comments to provide explanations of the main settings.

# Test Build Properties

# The name of the component. E.g. event, attribute, widget
component=test

# The list of files which should be concatenated to create the component.
# NOTE: For a css component (e.g. cssfonts, cssgrids etc.) use component.cssfiles instead.
# component.jsfiles=test.js, testHelperClass.js, testSubComponentClass.js
component.jsfiles=test.js

# The list of modules this component requires. Used to set up the Y.add module call for YUI 3.
component.requires=widget

# The list of modules this component supersedes. Used to set up the Y.add module call for YUI 3.
component.supersedes=

# The list of modules that are optional for this module. Used to set up the Y.add module call for YUI 3.
component.optional=

4. Connect Eclipse With YUI Builder and Module’s Build File

Creating and configuring a new Project Builder will allow Eclipse to drive the YUI Builder tool for your YUI 3 Custom Module.

Open Project Properties

Project > Properties

Screenshot showing the Project menu of Eclipse with Properties selected

Selecting Project > Properties in Eclipse

Create New Project [Ant] Builder

Builders > New…
Choose Ant Builder

Screenshot showing Eclipse’s New Project Builder window

Creating a new Project Ant Builder in Eclipse

Set Build Name and Select Buildfile Created in Step 3

Give the Builder a unique name and select the Buildfile associated with your Custom Module (which you setup previously) by choosing: Browse Workspace….

Screenshot of ‘Main’ tab of Eclipse’s Builder Configuration window

Setting Builder name and selecting Buildfile in Eclipse

Note: One finicky thing I found with Eclipse is it’s in-ability to remember Builder settings when moving between tabs; Always click Apply before switching tabs in the Builder Configuration.

Set Ant Targets

By default, the Builder’s: ‘After a “Clean”’, and ‘Manual Build’ Eclipse “Moments” will be set to the default Ant target as specified in the Ant buildfile. Choose the default target for ‘Auto Build’ as well.

Screenshot of ‘Targets’ tab in Eclipse’s Builder Configuration window

Setting the default Ant Target on Eclipse Builder for ‘Auto Build’

Set Build Options

Important here is to tell the builder which files to monitor for changes, and only re-build the YUI 3 Custom Module when one (or more) of those files has changed. Otherwise the build process will run in an infinite loop.

Check: Specify working set of relevant resources
Click: Specify Resources…

Screenshot of ‘Build Options’ tab in Eclipse’s Build Configuration window

Setting up relevant working resources for Eclipse Builder

Specify [Relevant] Resources

Notice: I do not have the build_tmp directory selected, just the source files that make up the YUI 3 Custom Module. You’ll want Eclipse to monitor just the module’s source files, and rebuild the module when only those file change.

Screenshot of ‘Specify Resources’ dialog for Eclipse Builder Configuration

Selecting the relevant resources for Eclipse to monitor

Build Project Automatically

Set your Eclipse project to ‘Build Automatically’ by selecting:
Project > Build Automatically

Screenshot showing the Project menu in Eclipse with Build Automatically selected

Selecting ‘Build Automatically’ in Eclipse’s Project menu

Further Configuration

The YUI Builder tool is highly-configurable, there are many configuration properties that can be set and Ant targets to run; both specified in the docs folder of the YUI Builder project. Most likely you’ll have to configure extra properties in your build.properties files to move your deployable YUI 3 Custom Modules to an appropriate place in your project’s layout.

I highly recommend you review the YUI Builder configuration properties and Ant targets documentation.

]]>
http://925html.com/techniques/auto-building-yui-3-custom-modules/feed/ 4
Simple Tab View with YUI 3 http://925html.com/code/simple-tab-view-with-yui-3/ http://925html.com/code/simple-tab-view-with-yui-3/#comments Mon, 04 Jan 2010 00:14:40 +0000 Eric Ferraiuolo http://925html.com/?p=456 Tab Views (i.e. tabbed-content) are ubiquitous on the web.

Examples of tabbed content areas from popular websites

Tabbed content is everywhere on the web

These interface elements are super simple to implement these days, so it seemed useful to show how a simple Tab View could be created using YUI 3.

The HTML Markup

The markup is a navigation list linking to content sections in the content area; simple and semantic. Some CSS classes are added to help with the layout, styling and JavaScript hooks.

<div class="tabview">

	<ul class="tabview-tabs">
		<li class="tabview-active"><a href="#content1">Tab 1</a></li>
		<li><a href="#content2">Tab 2</a></li>
		<li><a href="#content3">Tab 3</a></li>
	</ul>

	<div class="tabview-content">
		<div id="content1"><p>Content for Tab 1</p></div>
		<div id="content2" class="tabview-hidden"><p>Content for Tab 2</p></div>
		<div id="content3" class="tabview-hidden"><p>Content for Tab 3. bla bla bla…</p></div>
	</div>

</div>

The JavaScript Using YUI 3

Using the Y.Node Class made adding the behavior a breeze.

When a tab is clicked:

  1. I add the tabview-active CSS class to it and remove the class for all other tabs.
  2. I then find the corresponding content section and remove the tabview-hidden CSS class from it and make sure all the other content sections are hidden.
YUI().use('node', function(Y){

	Y.all('.tabview').each(function(){
		this.delegate('click', toggleTabs, '.tabview-tabs a');
	});

	function toggleTabs (e) {

		var tabview = e.container,
			tabs = tabview.all('.tabview-tabs li'),
			contents = tabview.all('.tabview-content > *'),
			tab = e.currentTarget.get('parentNode');

		contents.addClass('tabview-hidden')
			.item(tabs.removeClass('tabview-active').indexOf(tab.addClass('tabview-active')))
				.removeClass('tabview-hidden');

		e.preventDefault();
	}

});

Working Example

Screenshot of the Simple Tabs Example

The example using this code

View working example

]]>
http://925html.com/code/simple-tab-view-with-yui-3/feed/ 2
RGBA — IE Fallback http://925html.com/code/rgba-ie-fallback/ http://925html.com/code/rgba-ie-fallback/#comments Mon, 28 Dec 2009 21:00:12 +0000 Eric Ferraiuolo http://925html.com/?p=368 I’ve been using RGBA color values, border-radius, and box-shadow CSS3 features all over in my current projects. Since Internet Explorer (6, 7, & 8) does not support any CSS3 features I wanted to find some suitable fallbacks. RGBA color values are the most important (of the three CSS3 features I’m using) to find an IE-alternative; without one, IE will inherit the last non-RGBA color value (which maybe no color value, i.e. transparent).

A great article, Working With RGBA Colour, recently appeared on the collaborative web development blog: 24 Ways. The ‘Supporting All Browsers’ section of the article fell short of describing a robust way to “support” IE without punishing the other browsers.

Falling back to a PNG

In cases where you’re using transparency on a background-color (although not on borders or text) it’s possible to fall back to using a PNG with alpha channel to get the same effect. This is less flexible than using CSS as you’ll need to create a new PNG for each level of transparency required, but it can be a useful solution.

Using the same principal as before, we can specify the background in a style that all browsers will understand, and then overwrite it in a way that browsers without RGBA support will ignore.

h1 {
	background: transparent url(black50.png);
	background: rgba(0, 0, 0, 0.5) none;
}

Drew McLellan, 24 Ways: Working With RGBA Colour

I had a feeling, as did Martin Klepsch, that this code would result in downloading black50.png in browsers which wouldn’t even use/need it (i.e. non-IE browsers). Running tests and investigating alternate fallback techniques for RGBA color values in IE shined some light on this issue.

The Tests

I started with the fallback technique of using a PNG, as described in the 24 Ways post, as my baseline. I wanted to test the theory I had (and Martin Klepsch commented on): Browsers will download the PNG even if they do not use it.

I mocked up a quick-and-dirty example to test. I used a textured background for the page with a short paragraph of text centered on a semi-transparent green background (25% opacity); upon hovering over the paragraph, the background becomes more opaque (50% opacity).

A screenshot of the RGBA Test code

Screenshot of desired rendering

A screenshot of the RGBA Test code on hover

Screenshot of desired rendering on hover

Test 1: Baseline

I started with the following CSS, using the PNG technique:

body {
	background: url(pattern.png);
	font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
p {
	color: rgb(225, 225, 225);
	background: transparent url(green-25.png);
	background: rgba(156, 217, 107, 0.25) none;
	width: 400px;
	margin: 48px auto;
	padding: 32px 40px;
}
p:hover {
	background: transparent url(green-50.png);
	background: rgba(156, 217, 107, 0.50) none;
}

View Test 1

Safari 4 and Chrome 4 (Mac/PC) did download the unused PNG; while Firefox 3.x did not download the PNG. This method does work for IE 7 and IE 8; but it’s important to note: I noticed a FOUC while IE downloaded the PNG for the :hover state.

Test 2: Using !important

I wanted to test if setting !important on a rule would force the browser to ignore other rules with the same CSS property.

p {
	/* … */
	background: transparent url(green-25.png);
	background: rgba(156, 217, 107, 0.25) none !important;
	/* … */
}
p:hover {
	background: transparent url(green-50.png);
	background: rgba(156, 217, 107, 0.50) none !important;
}

View Test 2

No change from the baseline test. Bummer.

Test 3: !important with Reverse Order

For completeness I wanted to test if order made a difference when using !important

p {
	/* … */
	background: rgba(156, 217, 107, 0.25) none !important;
	background: transparent url(green-25.png);
	/* … */
}
p:hover {
	background: rgba(156, 217, 107, 0.50) none !important;
	background: transparent url(green-50.png);
}

View Test 3

No change from the baseline test. Webkit browsers are so eager to download these unused images.

Test 4: IE Conditional Comments

I use IE Conditional Comments on most projects, sending IE a set of “fixes” without resorting to using hacks in my CSS. I was confident this approach would work flawlessly.

<style type="text/css">

	body {
		background: url(pattern.png);
		font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
	}
	p {
		color: rgb(225, 225, 225);
		background: rgba(156, 217, 107, 0.25) none;
		width: 400px;
		margin: 48px auto;
		padding: 32px 40px;
	}
	p:hover {
		background: rgba(156, 217, 107, 0.50) none;
	}

</style>

<!--[if lte IE 8]>
	<style type="text/css">

		p {
			background: transparent url(green-25.png);
		}
		p:hover {
			background: transparent url(green-50.png);
		}

	</style>
<![endif]-->

View Test 4

It works! This technique is a great RGBA fallback for IE 7 and IE 8; Safari 4, Chrome 4, and Firefox 3.x did not download the unused PNGs. This outcome was expected since only IE is going to receive the style rules which set the background PNGs.

Test 5: Conditional Comments with CSS Filter

Searching for the Holy Grail in this one…

A major advantage of using RGBA color values is not needing to create and serve an alpha-transparent PNG. The RGBA IE fallback techniques looked at so far relied on a PNG. I wanted to test an approach which would not require a PNG. After doing some searching, I came across a post, RGBa Browser Support, using Microsoft’s IE-only Gradient Filter.

The tricky part in using Microsoft Gradient Filter is getting the Color Property set right.

Color is expressed in #AARRGGBB format, where AA is the alpha hexadecimal value, RR is the red hexadecimal value, GG is the green hexadecimal value, and BB is the blue hexadecimal value. The alpha value controls the opacity of the object. An alpha value of 00 is transparent, while a value of FF is opaque.

Color Property, Microsoft Developer Network

Determining The Alpha Hexadecimal Value

We need the figure out the AA part of #AARRGGBB. For CSS3 RGBA color values, A is a decimal/percentage of opacity; where 0.0 is completely transparent and 1.0 is completely opaque. Another way to look at it is like masking: where 0.0 is black or absence, and 1.0 is white or presence.

There are 256 values (0 — 255) for each color (red, green, and blue) in RGB values. We can multiply the alpha-opacity value by 256, round the result, and convert to hexadecimal to get our value.

e.g. For this example I have two alpha-opacity values: 0.25, and 0.50 on hover.
256 × 0.25 = 64
256 × 0.50 = 128
We can convert to hexadecimal values: 40, and 80 for hover.

<!--[if lte IE 8]>
	<style type="text/css">

		p {
			background: transparent;
			filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#408ED557,endColorstr=#408ED557);
		}
		p:hover {
			background: transparent;
			filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#808ED557,endColorstr=#808ED557);
		}

	</style>
<![endif]-->

View Test 5

This technique “worked” insofar that it used no PNGs and rendered in all the browsers tested. The nature of my test case using a :hover style revealed an IE rendering anomaly (surprise, surprise); in both IE 7 and 8 the :hover CSS filter was only applied when hovering the actual text of the paragraph instead of the region of space the paragraph takes up. In all the previous test cases this was never an issue; the problem is directly related to Microsoft’s IE-only Gradient Filter.

Microsoft’s CSS filter properties are generally seen as something to avoid using. I’m extremely curious about the performance trade-offs of an extra HTTP request vs. using the Microsoft Gradient filter property.

Results & Recommendations

My goal was to find a robust fallback for Internet Explorer’s lack of support for RGBA color values; a technique that wouldn’t punish the browsers which do support RGBA, and second, wouldn’t punish the developer.

Test 1 Test 2 Test 3 Test 4 Test 5
Safari 4 PNGs PNGs PNGs no PNGs no PNGs
Chrome 4 PNGs PNGs PNGs no PNGs no PNGs
Firefox 3.x no PNGs no PNGs no PNGs no PNGs no PNGs
IE 8 PNGs PNGs PNGs PNGs no PNGs
IE 7 PNGs PNGs PNGs PNGs no PNGs

Test 4: IE Conditional Comments proved to be the most robust; an excellent RGBA fallback solution for IE 7 and IE 8 that won’t download PNGs in Safari, Chrome, or Firefox.

Test 5: Conditional Comments with CSS Filter is a very compelling solution; it does not require PNGs in any browser, making it appealing to designers and developers. I caution the use of this technique: beware of rendering anomalies and/or unknown performance implications. This technique needs further testing an experimentation; that said, I still plan to give it a whirl in my current projects.

]]>
http://925html.com/code/rgba-ie-fallback/feed/ 1
Web Application Development with YUI3 http://925html.com/presentation/web-application-development-with-yui3/ http://925html.com/presentation/web-application-development-with-yui3/#comments Thu, 29 Oct 2009 22:04:10 +0000 Eric Ferraiuolo http://925html.com/?p=351 The slides and video from my talk at YUICONF2009.

This talk discusses techniques for deploying YUI 3 custom modules within a larger server application environment, using a working application as a reference. You’ll learn how to streamline your development and deployment process, ways to organize your code for maximum efficiency, how to integrate with the Eclipse IDE, and some tips and tricks around build strategies and server-side tooling.

Slides:

Video:

Web App Development with YUI 3 from Eric Ferraiuolo on Vimeo.
…The video is also on the YUI Theater.

]]>
http://925html.com/presentation/web-application-development-with-yui3/feed/ 1
Facebook Style Overlay in YUI 3 & CSS 3 http://925html.com/code/facebook-style-overlay-in-yui-3-css-3/ http://925html.com/code/facebook-style-overlay-in-yui-3-css-3/#comments Thu, 25 Jun 2009 03:48:44 +0000 Eric Ferraiuolo http://925html.com/?p=337 While wandering-around the Internet looking for examples of overlays in web application interfaces I thought of a challenge: create a working Facebook–styled overlay. I’m in the process of creating an application-wide design for overlays and needed some inspiration. Facebook uses overlays extensively and they have a distinct style [that others imitate, maybe even me :-) ]; I set-out to re-create this style.

Not wanting to mess around — I whipped up a working example of Facebook-styled overlays using only YUI 3 and CSS 3; things are nice and easy to do when you use the latest technologies.

The code is short enough I’ll just lay it all down here:

The JavaScript — YUI 3 is doing all the heavy lifting

YUI().use('overlay', 'dd-constrain', function(Y){

	var fbOverlay = new Y.Overlay({

		contentBox : '#fbOverlay',
		width : '540px',
		height : '300px',
		visible : false

	}).render();

	// make overlay draggable
	new Y.DD.Drag({

		node : fbOverlay.get('boundingBox'),
		handles : ['.yui-widget-hd']

	}).plug(Y.Plugin.DDConstrained, { constrain2view : true });

	// show overlay
	Y.get('#show-fbOverlay').on('click', function(e){
		if ( ! fbOverlay.get('visible') ) {
			fbOverlay.align(this, [Y.WidgetPositionExt.TL, Y.WidgetPositionExt.TR]);
			fbOverlay.show();
		}
	});

	// hide overlay
	Y.get('#hide-fbOverlay').on('click', function(e){
		fbOverlay.hide();
	});

});

Oh, and if you didn’t notice these Facebook–styled overlays are drag-able; Facebook doesn’t do that…

The CSS [3] — This is all me… and Facebook’s colors

#fbOverlay { display: none; }

.yui-widget #fbOverlay {
	display: block;
	background: rgba(0, 0, 0, 0.5);
	border-radius: 6px;
	-moz-border-radius: 6px;
	-webkit-border-radius: 6px;
	padding: 10px;
}
#fbOverlay .yui-widget-hd {
	border: #3B5998 1px solid;
	background: #6D84B4;
	color: #fff;
	padding: 0 10px;
	cursor: move;
}
#fbOverlay .yui-widget-bd {
	background: #fff;
	border: #555 1px solid;
	border-top: none;
	border-bottom : none;
	padding: 0 10px;
}
#fbOverlay .yui-widget-ft {
	border: #555 1px solid;
	border-top: none;
	background: #f2f2f2;
}
#fbOverlay .yui-widget-ft > div {
	border-top: #ccc 1px solid;
	padding: 5px 10px;
	text-align: right;
}

If you View Source you’ll see the contents of the overlay are in the (X)HTML; I’m making sure the hide these overlay–contents until the YUI Overlay Class instance grabs them up.

Not much to it; the only CSS 3 stuff is in the second rule, background: rgba(0, 0, 0, 0.5); giving the background some transparency and border-radius: 6px; to round-them-corners. Funny thing, these two style declarations save so much time and effort; look the source for Facebook’s overlay and you’ll see a nasty-mess of tables.

If you missed the link to the working version of the code above: here it is again.

]]>
http://925html.com/code/facebook-style-overlay-in-yui-3-css-3/feed/ 9
Adaptive CSS-Layouts: Compromising Ideals http://925html.com/techniques/adaptive-css-layouts-compromising-ideals/ http://925html.com/techniques/adaptive-css-layouts-compromising-ideals/#comments Mon, 15 Jun 2009 04:29:56 +0000 Eric Ferraiuolo http://925html.com/?p=331 Smashing Magazine recently published an article posing a question [as it's heading]:

Adaptive CSS-Layouts: New Era In Fluid Layouts?: “Fluid web designs have many benefits, but only if implemented correctly. With proper technique, a design can be seen correctly on large screens, small screens and even tiny PDA screens. With bad coding structure, however, a fluid layout can be disastrous. Because of this, we need to find ways to work around most, if not all, of the cons of fluid design.”

(Via Smashing Magazine.)

Intrigued, I read through the lengthly article; which, seemed to answer the posed question positively; or at least suggested the possibility of a yes answer.

The article is a prescription of six “effective techniques to create 100%-functional adaptive CSS-layouts” (quoted from article). These techniques are not independent from one another; they must be used together, and implemented correctly to achieve their prescribed solution to the Adaptive/Fluid layout problem.

All of these techniques can be implemented in one design to create a very user-friendly fluid layout. A smart use of the fluid grid can create an adaptable layout whose proportions remain faithful to the Rule of Thirds, balance and other design principles…

I’m left dissatisfied with Smashing Magazine’s solution. Spending time to understand and apply six different, seemingly independent, layout techniques to an interface feels too compromising. The one-size-fits-all approach to User Interface design leaves you compromising on crucial interface components and aspects of usability. The interface for a web site/application on a computer isn’t going to best serve a user on their iPhone; users with a computer screen resolution of 800×600 are rare; users with wide-screen monitors don’t browse at full screen.

Contorting the development of one interface to serve vastly different devices is not ideal. To achieve a better, more usable interface you must design for the device. I’m not buying the idea that we, as web developers and designers, can get away with forcing our compromised interfaces on user’s screens big and small.

I posed a somewhat opposing question about layout— regarding it’s flexibility, in October 2008: Layout Flexibility – Still A Requirement?

Questioning my own pervious layout techniques and use of Elastic Layouts (em’s to specifying box-model dimensions). Concluding that the adoption of full-page zoom in modern browsers leaves the elastic-layout technique obsolete.

]]>
http://925html.com/techniques/adaptive-css-layouts-compromising-ideals/feed/ 0
Writing HTML From JavaScript http://925html.com/code/writing-html-from-javascript/ http://925html.com/code/writing-html-from-javascript/#comments Wed, 15 Apr 2009 17:08:04 +0000 Eric Ferraiuolo http://925html.com/?p=326 JavaScript developers know the DOM API sucks: very verbose, and hairy across browsers. Some JavaScript library developers have chosen an innerHTML— approach for dynamically writing markup (DHTML). I’d argue this is just as crazy as trying to use the native DOM API.

Some [including me] are fed up with either alternative and decide to create a new API for writing markup in JavaScript:

Jeethu: “One of the sweetest parts of Mochikit IMO has been Mochikit.DOM. This is something which I’ve always missed with YUI. innerHTML is fast, but icky and it feels a little inelegant. So, I ended up writing something like Mochikit.DOM for YUI while writing Tagz. Thought it might be useful to others as well. So, here’s the mercurial repo with the code.”

(Via A Mochikit style Dombuilder for YUI | Jeethu’s Blog.)

Jeethu has something here; but I’m not psyched about the API design. The simple example on his blog post lacks clarity; the nested tree structure feels overly complicated. When creating a document in (X)HTML, nesting makes sense, you’re authoring the document as a whole thing. When programatically outputting (X)HTML, there isn’t a clear vision of the document as a whole. I prefer to keep with the semantics of programming when generating markup by writing meaningful statements.

At Oddnut Software, my small company (just two of us), we’re thinking about this issue with a broader vision: a unified API for writing XHTML on the server (Java) and on the client (JavaScript). We plan to open-source our project once it’s ready; we’re working out some of the final features.

]]>
http://925html.com/code/writing-html-from-javascript/feed/ 2
Webpage Thumbnails — Screenshots via Page Glimpse in JavaScript http://925html.com/code/webpage-thumbnails/ http://925html.com/code/webpage-thumbnails/#comments Tue, 14 Apr 2009 16:58:53 +0000 Eric Ferraiuolo http://925html.com/?p=306 For quite some time I’ve had a desire to fetch screenshots of webpages in thumbnail form. My last round of development in the area involved a somewhat overly complex solution using Amazon’s AWS Alexa Site Thumbnail service. I chose to integrate with the AWS Alexa Thumbnail service over other services because it just returned an image, no extra crap (Snap’s thumbnails are grotesque) and no ads. Although the service wasn’t free, it only cost a few pennies to use.

The other requirement I have is to retrieve the thumbnails in JavaScript. This lead to the creation my Ajax Alexa Thumbnails project. The AWS Alexa Thumbnail service required a client to interact via a XML web service (similar to the other AWS services), this means signing the request with your AWS credentials; something I wasn’t going to do in JavaScript. The project became too complex for the task; it involved making an Ajax request to a local PHP file which dealt with sending the request to and receiving the response from Amazon. Crazy I know, which is why I’m deprecating the Ajax Alexa Thumbnails project along with the deprecation of the AWS Alexa Thumbnail service in favor of my new solution [below] using Page Glimpse.

Page Glimpse

On March 18, 2009 I received the email from Amazon AWS telling me the Alexa Site Thumbnail service is being deprecated:

Dear Alexa Developer,

We are announcing the deprecation of the Alexa Site Thumbnail service as of March 13, 2009. After this date, the service will be closed to new subscriptions. The Alexa Site Thumbnail service will continue to be operational for existing subscribers for 90 days, until June 12, 2009.

Use of the service has been relatively low, and we have decided to focus our resources on more broadly used services in order to provide the greatest benefit to Alexa customers.

Thank you for your use of the service. We regret any inconvenience to you.

Thank you,

The Alexa Web Services Team

Luckily that same day TechCrunch ran an article about this announcement and users posted alternate services in the comments, which included Page Glimpse. Based on Marcio Castilho comment, I figured it was worth a look:

You should check http://www.pageglimpse.com. It is also run on scalable Amazon AWS infrastructure, and it is much more faster than Alexa and other services.

Page Glimpse is a free service providing programmatic access to thumbnails of any web page (AWS Alexa Site Thumbnail service just provided thumbnails of hostnames, not interior pages). They have a well-documented RESTful HTTP API; to retrieve a thumbnail for a webpage you construct a URL to their API with query-string parameters. If Page Glimpse hasn’t captured the webpage you are requesting, it will do so, automatically, within a few seconds. They also have API methods to check if a thumbnail already exists, and request the service to capture a thumbnail. Just like Marcio stated, Page Glimpse is very fast; I’ve been pretty amazed at the speed durning my testing.

To start using Page Glimpse, Sign-up for a free account to get your developer key.

After signing-up and trying a few requests with my shinny new developer key I was pretty curious how they are offering this service for free. I gathered from their Terms of Use that if you are a heavy user (> 300 GB/month) you might have to pay a monthly fee (this seems completely reasonable):

If your bandwidth usage exceeds 300 GB/month, or significantly exceeds the average bandwidth usage (as determined solely by RADSense) of other PageGlimpse customers, we reserve the right to terminate your use of the service, but we will make the best effort possible to accomodate the increased bandwidth, although a monthly fee might apply in this case.

Page Glimpse Thumbnails in JavaScript

With the Page Glimpse API being HTTP based and free, I am okay with including my developer key in client-side JavaScript code; it’s just not worth the complexity to hide it.

One could easily use Page Glimpse in JavaScript by constructing a new img DOM Node and set it’s href property to the Page Glimpse API URL with all the parameters. I want something more convenient. Even with Page Glimpse being a speedy service, I also want to make sure the image has fully downloaded before showing it on the page.

Thumbnailsjs — JavaScript Interface To Page Glimpse’s API

I’ve created a small (1.2KB minified, 0.7KB gzipped) utility Class to provide a convenient interface to Page Glimpse in JavaScript and to ensure the images have been fully-downloaded by the client before displaying the it on the page (thanks to Luke Smith’s Is my image loaded? post).

The fully-documented source code along with a simple example is available in the files section: http://925html.com/files/thumbnailsjs/.

Example of Client Code
var container = document.getElementById('container'),
	thumbs = Thumbnails({ devkey:'xxx' });

thumbs.get('http://google.com', append);
thumbs.get([
	'http://eric.ferraiuolo.name/',
	'http://925html.com',
	'http://oddnut.com'
], append);

function append ( url, img ) {
	var link = document.createElement('a');
	link.href = url;
	link.appendChild(img);
	container.appendChild(link);
}
Details of Thumbnailsjs API

The Class’ constructor takes a config Object which can have the following properties:

  • devkey {String}: Your PageGlimpse API deveoper key. All requests to PageGlimpse service require a devkey (required).
  • size {String}: The size of thumbnail. Available sizes are: small, medium, large.
  • root {Boolean}: Indicates if the thumbnails for the domain root should be displayed. The root thumbnail image will only be used if an interior page’s thumbnail hasn’t been resolved.
  • nothumb {String}: If the thumbnail for the website is not yet taken, the URL for this property will be used. If this parameter is not set a PageGlimpse default image will be returned.

The Class only has one public method, get, which takes two arguments (both required):

  1. url {String | Array}: Location of webpage(s)
  2. callback {Function}: Function to pass the webpage URL and image Node once fully downloaded

Page Glimpse + Thumbnailsjs

I’m planning to update my personal webpage to use this setup for displaying tooltips for external links with a thumbnail of the webpage; retiring the current setup of using AWS Alexa Site Thumbnail + Ajax-Alexa-Thumbnails.

I have to say to the folks over at RADSense Software who put together Page Glimpse; Thanks for developing such a cool and useful service!

Hope you find Page Glimpse + Thumbnailjs useful; let me know if you questions, thoughts, or requests.

]]>
http://925html.com/code/webpage-thumbnails/feed/ 20
Smart Polling http://925html.com/code/smart-polling/ http://925html.com/code/smart-polling/#comments Mon, 30 Mar 2009 16:31:44 +0000 Eric Ferraiuolo http://925html.com/?p=288 I’m working on the rich UI of a small web application which has a problem I need to solve: the data from the server which the UI is presenting could change at any time. The first iteration of the UI has a refresh button. Clicking this button sends an Ajax request to a resource on the server which responds with a JSON data structure, and the UI is updated with any changes in the new data. My business partner doesn’t like the refresh button; he questions why it’s there and states how annoying it is to press the button all the time. His suggestion is the rich UI should smartly poll the server for changes to the data, and update the UI automatically.

Intrigued by his idea, we continued our discussion leading to a definition for what it means to smartly poll a server’s resource:

  • Use conditional GET requests
  • Retain the most recent Etag and Last-Modified date of the polled resource
  • Disable polling when the browser window is inactive

Implementing a smart polling process in our application’s rich UI gives us some desired benefits:

  • Removal of the refresh button
  • Automatic updating of the UI when the resource on the server has changed
  • Less repainting of the page since the DOM is touched only when the data has changed
  • Changes to the UI only happen when the window is active (the user sees them) as polling is paused while the user is doing something else

Creating a reusable component to achieve a smart polling process feels like the correct approach as I foresee a need to use this functionality in future projects as well.

Poller – YUI3 Component

Update: The IO Poller Module lives on the YUILibrary Gallery

I’ve moved the code to the YUI 3 Gallery on YUILibrary site; IO Poller on the YUILibrary Gallery.

The rest of this post is out-dated (comments are still good though)…

The remainder of this post pertains to Poller, a YUI3 compatible component hosted in the files sections of this site: http://925html.com/files/smart_polling/ (a Bazaar branch)

I’ve been developing several components and widgets using the new YUI3 component infrastructure; this is the first fully built and documented (including docs for the relevant YUI modules) one I’m releasing.

The Poller class is 3.3kb minified and 1.1kb gzipped, it requires io-base and base YUI3 modules.

The Poller class’ usage is through the public API methods: pause, sendRequest, start and stop; along with events: modified, request, response, start and stop.

Internally the Poller class is using conditional XHR GET requests. When polling is first kicked off (i.e. start() is called) the Etag and Last-Modified header values are cached. As subsequent polling requests are sent to the server the cached Etag is set for the If-None-Match HTTP request header, if no Etag was cached (i.e. the server doesn’t send Etags) then the cached Last-Modified date is set as the If-Modified-Since header. If a server doesn’t support conditional GET requests at all, sending neither an Etag nor a Last-Modified date, the Poller class will fire the modified event with every response.

Example instance of Poller

var myData = new Y.Poller({ url:'path/to/resource/', interval:5000 });
myData.on( 'modified', updateUI );
myData.start();

The above code will continue sending Ajax requests to the resource at path/to/resource/ until myData.stop() is called. If the Poller receives a 200 HTTP status (and not a 304 not modified status) the modified event will be fired passing the txId, response and args to the updateUI subscriber function. Subscribing to the poller:modified event is like subscribing to Y.io’s io:success event.

Disable polling on inactive windows

With the above functionality covering most of our requirements, we still need a way to pause polling when the browser window isn’t active. We came up with the idea to attach listeners to the window focus and blur events which call into protected methods to start/stop the polling process. Note: this functionality won’t interfere with the client code’s intent. i.e. if the client explicitly calls stop(), losing (blur) then gaining focus of the browser window won’t automatically start the polling process again. It respects the state of the Poller’s polling read-only attribute; calling start() will set the polling attribute to true and calling stop() will set the attribute to false.

To enable the functionality to pause and resume polling on the window’s focus and blur events: either set the pauseInactive attribute, or declare pauseInactive: true on the config object during construction.

Using the myData Poller instance from above:

myData.set( 'pauseInactive', true );

Setting the paseInactive attribute on construction:

var myOtherData = new Y.Poller({
	url : 'path/to/resource/',
	interval : 5000,
	pauseInactive : true
});

Putting it all together

I have a small demo for this project which continually checks a resource on the server to see if it has changed and updates the page if it has. The resource is a super simple JSON object with two properties: label and time, where time is a timestamp indicating the time on the server when the file was last modified.

{ "label":"the server's time was", "time":1238530883810 }

There is a button on the page which will update the data file stamping the new time on the server as the value of the time property. You can press this button to update the data file and after the next polling request is sent the UI will be updated to reflect the change in the file. I’ve included a YUI Console on the page which I send log messages to so you can see what’s going on.

The following is the code pertaining to the Poller instance used in the demo:

var poller = new Y.Poller({
	url : 'data.json',
	headers : {"foo":"bar"},
	interval : 7000,
	pauseInactive : true
});
poller.on( 'request', logRequestData );
poller.on( 'response', logResponseData );
poller.on( 'modified', updateUI );
poller.start();

Run The Demo

Note: I’ve have some issues running the demo on IE8 and IE7, it has something to do with the YUI Console; enabling the Developer Tools in IE8 made the demo work correctly.

]]>
http://925html.com/code/smart-polling/feed/ 22