O'Reilly

Create a Download Package Using MooTools Moousture

By on  
Christina Ricci

Zohaib Sibt-e-Hassan recently released a great mouse gestures library for MooTools called Moousture. Moousture allows you to trigger functionality by moving your mouse in specified custom patterns. Too illustrate Moousture's value, I've created an image download builder using Mooustures and PHP.

The XHTML

<div id="messager">Waiting for you to select images...</div>
<img src="preload-images/1.jpg" alt="Image 1" width="100" class="moouse" />
<img src="preload-images/2.jpg" alt="Image 2" width="100" class="moouse" />
<img src="preload-images/3.jpg" alt="Image 3" width="100" class="moouse" />
<img src="preload-images/4.jpg" alt="Image 4" width="100" class="moouse" />
<img src="preload-images/5.jpg" alt="Image 5" width="100" class="moouse" />
<img src="preload-images/6.jpg" alt="Image 6" width="100" class="moouse" />
<img src="preload-images/7.jpg" alt="Image 7" width="100" class="moouse" />
<img src="preload-images/8.jpg" alt="Image 8" width="100" class="moouse" />
<div style="clear:both;"></div>
<input type="submit" name="download" id="download" class="button" value="Download Package" />

We provide the images, a messenger DIV which lest the user know when an image has been added and removed, and a submit button to generate the file.

The CSS

#messager	{ padding:10px; border:2px solid #fc0; background:#fffea1; margin-bottom:10px; }
.unselected	{ border:2px solid #ccc; background:#fff; }
.selected	{ border:2px solid #3b5998; background:lightblue; }
.moouse		{ width:100px; float:left; margin:0 20px 0 0; padding:5px; }

Nothing special, except that the selected class should make it very apparent to the user that the image has been selected.

The MooTools JavaScript

/* dom ready */
window.addEvent('domready',function() {
	/* add to each image */
	$$('img.moouse').each(function(img) {
		/* add unselected class */
		img.addClass('unselected');
		/* create the gesture */
		var gest = new Moousture.ReducedLevenMatcher();
		/* set the "add" and "remove" gestures */
		gest.addGesture([4,5,6,7,0,1,2,3], function(error) {
			/* good gesture! */
			if(error < 3) {
				img.addClass('selected');
				document.id('messager').set('text','Image ' + img.get('alt') + ' added.');
			}
			/* bad! */
			else {
				//document.id('messager').set('text','Sorry - we couldn\'t detect that you wanted: ' + img.get('alt') + '.');
			}
		}); //add
		/* set the "add" and "remove" gestures */
		gest.addGesture([4,3,2,1,0,7,6,5], function(error) {
			/* good gesture! */
			if(error < 3) {
				if(img.hasClass('selected')) {
					img.removeClass('selected');
					document.id('messager').set('text','Image ' + img.get('alt') + ' removed.');
				}
			}
			/* bad! */
			else {
				//document.id('messager').set('text','Sorry - we couldn\'t detect that you wanted: ' + img.get('alt') + '.');
			}
		}); //add
		/* add the probe to the image */
		var probe = new Moousture.MouseProbe(img);
		/* create the recorder */
		recorder = new Moousture.Recorder({maxSteps: 20, minSteps: 8, matcher: gest});
		/* monitor the image */
		monitor = new Moousture.Monitor(30, 2);
		/* start monitoring */
		monitor.start(probe, recorder);
	});
	
	/* submission */
	document.id('download').addEvent('click',function() {
		/* get the selected images */
		var selected = $$('img.selected');
		if(selected.length) {
			var qstring = '?images=';
			/* for each image */
			selected.each(function(img) {
				/* add to qstring */
				qstring += img.get('src') + ';';
			});
			/* force download */
			window.location = 'moousture.php' + qstring;
		}
	});
	
});

When the DOM is ready, we set up two gesture patterns for each image -- one to select an image, one to unselect the image. When the user has selected the images they want and click the submit button, Moo redirects them to a PHP address with the images in the $_GET['image'] variable.

Note that the "error" threshold I've set is 3. If a user attempts a gesture on an image, an error number is automatically calculated. This error number represents how different the user's gesture was than the given gesture listener.

The PHP

/* if we got images */
if($_GET['images']) {
	/* split the querystring */
	$images = explode(';',$_GET['images']);
	$valid_files = array();
	/* for every image */
	foreach($images as $image) {
		/* if the image exists */
		if(file_exists($image)) {
			/* add it to our good image list */
			$valid_files[] = $image;
		}
	}
	/* if we have good images */
	if(count($valid_files)) {
		/* files */
		$destination_folder = 'image-packages';
		$filename = time().'.zip';
		$path = $destination_folder.'/'.$filename;
		/* create zip! */
		$zip = new ZipArchive();
		if($zip->open($path,ZIPARCHIVE::CREATE) !== true) {
			return false;
		}
		/* add the files */
		foreach($valid_files as $file) {
			$zip->addFile($file,$file);
		}
		/* close the zip */
		$zip->close();
		/* force download zip */
		if(ini_get('zlib.output_compression')) { ini_set('zlib.output_compression', 'Off');	}
		header('Pragma: public');
		header('Expires: 0');
		header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
		header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($path)).' GMT');
		header('Cache-Control: private',false);
		header('Content-Type: application/zip');
		header('Content-Disposition: attachment; filename="package.zip"');
		header('Content-Transfer-Encoding: binary');
		header('Content-Length: '.filesize($path));
		header('Connection: close');
		readfile($path);
		exit();
	}
	die();
}

We receive the image paths via $_GET['images']. Once we validate that each image exists, we build the ZIP file and force-download it to the user.

This solution isn't great when there are many images available; a POST method (via form submission) would be best for that situation. What are your thoughts on Moousture though? Any suggestions for the creator?

Track.js Error Reporting

Recent Features

  • CSS Filters

    CSS filter support recently landed within WebKit nightlies. CSS filters provide a method for modifying the rendering of a basic DOM element, image, or video. CSS filters allow for blurring, warping, and modifying the color intensity of elements. Let's have...

  • 5 Ways that CSS and JavaScript Interact That You May Not Know About

    CSS and JavaScript:  the lines seemingly get blurred by each browser release.  They have always done a very different job but in the end they are both front-end technologies so they need do need to work closely.  We have our .js files and our .css, but...

Incredible Demos

  • Fixing sIFR Printing with CSS and MooTools

    While I'm not a huge sIFR advocate I can understand its allure. A customer recently asked us to implement sIFR on their website but I ran into a problem: the sIFR headings wouldn't print because they were Flash objects. Here's how to fix...

  • Retrieve Your Gmail Emails Using PHP and IMAP

    Grabbing emails from your Gmail account using PHP is probably easier than you think. Armed with PHP and its IMAP extension, you can retrieve emails from your Gmail account in no time! Just for fun, I'll be using the MooTools Fx.Accordion plugin...

Discussion

  1. the demo doesn’t seem 2 wrk, anyway cool example!

  2. Fabian

    Nice idea, but nothing for the wild. I would prefer some selection like you can do it within your explorer (for example).

  3. no sorry, it was me.. the demo works fine.. :)

  4. Ben

    Cool, but impractical

  5. Probably impractical today, but will have much more use in the future when people aren’t using mouses.

  6. Damn, circling images is hardly a good way to select them…how ’bout just a normal click?

    It took me quite a while to select 4 images…that’s after figuring out that I had to circle them…

  7. Jay

    Even if the mouse were to be replaced with a different device (say like Minority Report), in this particular case, it’s still easier to just “touch” (select) the image, rather than have to draw circles around each image. If in the future we have to draw small circles around everything we want to interact with, I’ll have to remain living in the past. ;)

    If I could select more than one image with a larger circle, that may be a bit more useful, but it’s still imprecise, and the lack of feedback (perhaps a trail of what my circle looks like would help) can make it a bit frustrating. Just moving the mouse around the page (and not actually trying to select anything) can cause images to be selected/de-selected.

    I know it’s just a tech demo, but perhaps some criticism can help push the demo into something more practical. :)

  8. the first time look the demo didn’t work but when i noticed that the number above is changing then i hit the download it works.. nice idea thanks.

  9. @Jay: Very valid point. A multi-grab would be awesome. I’ll take a look at that — thanks for the idea.

  10. Chris Bolson

    Not having much luck in Firefox 3.5 on the mac.
    Managed to select an image after several attempts but no luck in unselecting.

    Must admit that a quick click is simpler ;)

  11. Ah, got it, you need to draw the circle “on” the image and not “around” it.
    Works perfectly like that. Maybe the instructions could be a bit clearer ;)

  12. I’m waiting for someone to write some kind of Javascript game with this technology. Seems like the more likely use and would be quite cool.

  13. DJ

    Nope, no matter what I try – doesn’t work! circling in such tight quarters cumbersome at best – should be a much more efficient way of selecting than making a full circle around it.

  14. I like checkboxes better because they work.

  15. Darkimmortal

    Wow, super-intuitive. Maybe even worse than the likes of FancyForm…

  16. when i download zip file from Internet Explorer that will not open and gives error.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!

Recently on David Walsh Blog

  • OSCON Portland:  Conference  Discount!

    O'Reilly puts on the best web industry conferences in the world.  These conferences include Fluent Conference, Velocity Conference, and the upcoming OSCON in Portland, Oregon from July 20-24.  Open Source Convention (OSCON) is a conference that focuses specifically on open source developers and the tools and possibilities...

  • Follow Redirects with cURL

    I love playing around with cURL. There's something about loading websites via command line that makes me feel like some type of smug hacker, just like tweeting from command line does. I recently cURL'd the Google homepage and saw the following: I found it weird that Google...

  • Developers Have WordPress, Amateurs Have Squarespace, Professional Designers Have the NEW Webydo!

    Web design platforms have traditionally come in one of two varieties. There are the solutions like WordPress and Drupal that are incredibly powerful, but an understanding of web development and coding is required to be able to use those platforms effectively. On the other side of the...

  • Chris Coyierâs Favorite CodePen Demos II

    Hey everyone! Before we get started, I just want to say it’s damn hard to pick this few favorites on CodePen. Not because, as a co-founder of CodePen, I feel like a dad picking which kid he likes best (RUDE). But because there is just so...

  • GSAP + SVG For Power Users: Motion Along A Path

    Now that the GreenSock API is picking up steam, there are many tutorials and Getting Started guides out there to provide good introductions to the library, not to mention GreenSock’s own Forum and Documentation. This article isn’t intended for beginners, but rather a...