text: // jquery.jparallax.js
  // 0.9.1
  // Stephen Band
  //
  // Dependencies:
  // jQuery 1.2.6 (jquery.com)
  //
  // Project and documentation site:
  // http://webdev.stephband.info/parallax.html
  
  
  // CLOSURE
  
  (function(jQuery) {
  
  
  // PRIVATE FUNCTIONS
  
  function stripFiletype(ref) {
    var x=ref.replace('.html', '');
    return x.replace('#', '');
  }
  
  function initOrigin(l) {
    if (l.xorigin=='left')	{l.xorigin=0;}	else if (l.xorigin=='middle' || l.xorigin=='centre' || l.xorigin=='center')	{l.xorigin=0.5;}	else if (l.xorigin=='right')	{l.xorigin=1;}
    if (l.yorigin=='top')		{l.yorigin=0;}	else if (l.yorigin=='middle' || l.yorigin=='centre' || l.yorigin=='center')	{l.yorigin=0.5;}	else if (l.yorigin=='bottom')	{l.yorigin=1;}
  }
  
  function positionMouse(mouseport, localmouse, virtualmouse) {
  
    var difference = {x: 0, y: 0, sum: 0};
  
  	// Set where the virtual mouse is, if not on target
    if (!mouseport.ontarget) {
  
      // Calculate difference
      difference.x    = virtualmouse.x - localmouse.x;
      difference.y    = virtualmouse.y - localmouse.y;
      difference.sum  = Math.sqrt(difference.x*difference.x + difference.y*difference.y);
  
      // Reset virtualmouse
      virtualmouse.x = localmouse.x + difference.x * mouseport.takeoverFactor;
      virtualmouse.y = localmouse.y + difference.y * mouseport.takeoverFactor;
  
      // If mouse is inside the takeoverThresh set ontarget to true
      if (difference.sum < mouseport.takeoverThresh && difference.sum > mouseport.takeoverThresh*-1) {
      	mouseport.ontarget=true;
      }
    }
    // Set where the layer is if on target
    else {
      virtualmouse.x = localmouse.x;
      virtualmouse.y = localmouse.y;
    }
  }
  
  function setupPorts(viewport, mouseport) {
  
  	var offset = mouseport.element.offset();
  
    jQuery.extend(viewport, {
      width: 		viewport.element.width(),
      height: 	viewport.element.height()
    });
  
    jQuery.extend(mouseport, {
      width:		mouseport.element.width(),
      height:		mouseport.element.height(),
      top:			offset.top,
      left:			offset.left
    });
  }
  
  function parseTravel(travel, origin, dimension) {
  
    var offset;
    var cssPos;
  
    if (typeof(travel) === 'string') {
      if (travel.search(/^\d+\s?px$/) != -1) {
        travel = travel.replace('px', '');
        travel = parseInt(travel, 10);
        // Set offset constant used in moveLayers()
        offset = origin * (dimension-travel);
        // Set origin now because it won't get altered in moveLayers()
        cssPos = origin * 100 + '%';
        return {travel: travel, travelpx: true, offset: offset, cssPos: cssPos};
      }
      else if (travel.search(/^\d+\s?%$/) != -1) {
        travel.replace('%', '');
        travel = parseInt(travel, 10) / 100;
      }
      else {
        travel=1;
      }
    }
    // Set offset constant used in moveLayers()
    offset = origin * (1 - travel);
    return {travel: travel, travelpx: false, offset: offset}
  }
  
  function setupLayer(layer, i, mouseport) {
  
    var xStuff;
    var yStuff;
    var cssObject = {};
  
    layer[i]=jQuery.extend({}, {
    	width:		layer[i].element.width(),
    	height:		layer[i].element.height()
    }, layer[i]);
  
    xStuff = parseTravel(layer[i].xtravel, layer[i].xorigin, layer[i].width);
    yStuff = parseTravel(layer[i].ytravel, layer[i].yorigin, layer[i].height);
  
    jQuery.extend(layer[i], {
    	// Used in triggerResponse
    	diffxrat:    mouseport.width / (layer[i].width - mouseport.width),
    	diffyrat:    mouseport.height / (layer[i].height - mouseport.height),
    	// Used in moveLayers
    	xtravel:     xStuff.travel,
    	ytravel:     yStuff.travel,
    	xtravelpx:   xStuff.travelpx,
    	ytravelpx:   yStuff.travelpx,
    	xoffset:     xStuff.offset,
    	yoffset:     yStuff.offset
    });
  
    // Set origin now if it won't be altered in moveLayers()
    if (xStuff.travelpx) {cssObject.left = xStuff.cssPos;}
    if (yStuff.travelpx) {cssObject.top = yStuff.cssPos;}
    if (xStuff.travelpx || yStuff.travelpx) {layer[i].element.css(cssObject);}
  }
  
  function setupLayerContents(layer, i, viewportOffset) {
  
    var contentOffset;
  
    // Give layer a content object
    jQuery.extend(layer[i], {content: []});
    // Layer content: get positions, dimensions and calculate element offsets for centering children of layers
    for (var n=0; n<layer[i].element.children().length; n++) {
  
  	  if (!layer[i].content[n])          layer[i].content[n]             = {};
  	  if (!layer[i].content[n].element)  layer[i].content[n]['element']  = layer[i].element.children().eq(n);
  
  	  // Store the anchor name if one has not already been specified.  You can specify anchors in Layer Options rather than html if you want.
      if(!layer[i].content[n].anchor && layer[i].content[n].element.children('a').attr('name')) {
      	layer[i].content[n]['anchor'] = layer[i].content[n].element.children('a').attr('name');
  	  }
  
  	  // Only bother to store child's dimensions if child has an anchor.  What's the point otherwise?
  	  if(layer[i].content[n].anchor) {
        contentOffset = layer[i].content[n].element.offset();
  	  	jQuery.extend(layer[i].content[n], {
  	  		width: 		layer[i].content[n].element.width(),
  	  		height:		layer[i].content[n].element.height(),
  	  		x:			  contentOffset.left - viewportOffset.left,
  	  		y:			  contentOffset.top - viewportOffset.top
  	  	});
  	  	jQuery.extend(layer[i].content[n], {
  	  	  posxrat:  (layer[i].content[n].x + layer[i].content[n].width/2) / layer[i].width,
  	  	  posyrat:  (layer[i].content[n].y + layer[i].content[n].height/2) / layer[i].height
        });
  	  }
    }
  }
  
  function moveLayers(layer, xratio, yratio) {
  
  	var xpos;
  	var ypos;
  	var cssObject;
  
  	for (var i=0; i<layer.length; i++) {
  
      // Calculate the moving factor
    	xpos = layer[i].xtravel * xratio + layer[i].xoffset;
      ypos = layer[i].ytravel * yratio + layer[i].yoffset;
      cssObject = {};
    	// Do the moving by pixels or by ratio depending on travelpx
      if (layer[i].xparallax) {
        if (layer[i].xtravelpx) {
          cssObject.marginLeft = xpos * -1 + 'px';
        }
        else {
          cssObject.left = xpos * 100 + '%';
          cssObject.marginLeft = xpos * layer[i].width *-1 + 'px';
        }
  	  }
  	  if (layer[i].yparallax) {
        if (layer[i].ytravelpx) {
          cssObject.marginTop = ypos * -1 + 'px';
        }
        else {
          cssObject.top = ypos * 100 + '%';
          cssObject.marginTop = ypos * layer[i].height * -1 + 'px';
        }
      }
      layer[i].element.css(cssObject);
  	}
  }
  
  // PLUGIN DEFINITION **********************************************************************
  
  jQuery.fn.jparallax = function(options) {
  
  	// Organise settings into objects (Is this a bit of a mess, or is it efficient?)
  	var settings = jQuery().extend({}, jQuery.fn.jparallax.settings, options);
  	var settingsLayer = {
    			xparallax:				settings.xparallax,
    			yparallax:				settings.yparallax,
    			xorigin:					settings.xorigin,
    			yorigin:					settings.yorigin,
    			xtravel:          settings.xtravel,
    			ytravel:          settings.ytravel
    		};
    var settingsMouseport = {
    			element:					settings.mouseport,
  				takeoverFactor:		settings.takeoverFactor,
  				takeoverThresh:		settings.takeoverThresh
  			};
  	if (settings.mouseport) settingsMouseport['element'] = settings.mouseport;
  
  	// Populate layer array with default settings
  	var layersettings = [];
  	for(var a=1; a<arguments.length; a++) {
  		layersettings.push( jQuery.extend( {}, settingsLayer, arguments[a]) );
  	}
  
  	// Iterate matched elements
  	return this.each(function() {
  
      // VAR
  
  		var localmouse = {
  					x:				0.5,
  					y:				0.5
  		};
  
      var virtualmouse = {
  					x:				0.5,
  					y:				0.5
  		};
  
  		var timer = {
  		  running:		false,
  		  frame:			settings.frameDuration,
  		  fire:				function(x, y) {
  		  	  				  positionMouse(mouseport, localmouse, virtualmouse);
                      moveLayers(layer, virtualmouse.x, virtualmouse.y);
  		  	  				  this.running = setTimeout(function() {
  		  	  				  	if ( localmouse.x!=x || localmouse.y!=y || !mouseport.ontarget ) {
  		  	  				  		timer.fire(localmouse.x, localmouse.y);
  		  	  				  	}
  		  	  				  	else if (timer.running) {
  		  	  				  		timer.running=false;
  		  	  				  	}
  		  	  				  }, timer.frame);
  		  	  				}
  		};
  
  		var viewport	=	{element: jQuery(this)};
  
  		var mouseport = jQuery.extend({}, {element: viewport.element}, settingsMouseport, {
  		  xinside:          false,		// is the mouse inside the mouseport's dimensions?
  			yinside:	        false,
  			active:		        false,		// are the mouse coordinates still being read?
  			ontarget:         false			// is the top layer inside the takeoverThresh?
  		});
  
  		var layer			= [];
  
      // FUNCTIONS
  
      function matrixSearch(layer, ref, callback) {
        for (var i=0; i<layer.length; i++) {
          var gotcha=false;
          for (var n=0; n<layer[i].content.length; n++) {
            if (layer[i].content[n].anchor==ref) {
              callback(i, n);
              return [i, n];
            }
          }
        }
        return false;
      }
  
      // RUN
  
      setupPorts(viewport, mouseport);
  
  		// Cycle through and create layers
      for (var i=0; i<viewport.element.children().length; i++) {
  			// Create layer from settings if it doesn't exist
  			layer[i]=jQuery.extend({}, settingsLayer, layersettings[i], {
  				element:	viewport.element.children('*:eq('+i+')')
  			});
  
  		  setupLayer(layer, i, mouseport);
  
        if (settings.triggerResponse) {
  		    setupLayerContents(layer, i, viewport.element.offset());
  		  }
  		}
  
  
  
      // Set up layers CSS and initial position
      viewport.element.children().css('position', 'absolute');
  		moveLayers(layer, 0.5, 0.5);
  
  		// Mouse Response
  		if (settings.mouseResponse) {
  			jQuery().mousemove(function(mouse){
  				// Is mouse inside?
  				mouseport.xinside = (mouse.pageX >= mouseport.left && mouse.pageX < mouseport.width+mouseport.left) ? true : false;
  				mouseport.yinside = (mouse.pageY >= mouseport.top  && mouse.pageY < mouseport.height+mouseport.top)  ? true : false;
  				// Then switch active on.
  				if (mouseport.xinside && mouseport.yinside && !mouseport.active) {
  					mouseport.ontarget = false;
  					mouseport.active = true;
  				}
  				// If active is on give localmouse coordinates
  				if (mouseport.active) {
  					if (mouseport.xinside) { localmouse.x = (mouse.pageX - mouseport.left) / mouseport.width; }
  					else { localmouse.x = (mouse.pageX < mouseport.left) ? 0 : 1; }
  					if (mouseport.yinside) { localmouse.y = (mouse.pageY - mouseport.top) / mouseport.height; }
  					else { localmouse.y = (mouse.pageY < mouseport.top) ? 0 : 1; }
  				}
  
  				// If mouse is inside, fire timer
  				if (mouseport.xinside && mouseport.yinside)  { if (!timer.running) timer.fire(localmouse.x, localmouse.y); }
  				else if (mouseport.active) { mouseport.active = false; }
  			});
  		}
  
  		// Trigger Response
  		if (settings.triggerResponse) {
  		  viewport.element.bind("jparallax", function(event, ref){
  
  		    ref = stripFiletype(ref);
  
          matrixSearch(layer, ref, function(i, n) {
            localmouse.x = layer[i].content[n].posxrat * (layer[i].diffxrat + 1) - (0.5 * layer[i].diffxrat);
            localmouse.y = layer[i].content[n].posyrat * (layer[i].diffyrat + 1) - (0.5 * layer[i].diffyrat);
  
            if (!settings.triggerExposesEdges) {
              if (localmouse.x < 0) localmouse.x = 0;
              if (localmouse.x > 1) localmouse.x = 1;
              if (localmouse.y < 0) localmouse.y = 0;
              if (localmouse.y > 1) localmouse.y = 1;
            }
  
            mouseport.ontarget = false;
  
            if (!timer.running) timer.fire(localmouse.x, localmouse.y);
          });
  		  });
  		}
  
  		// Window Resize Response
  		jQuery(window).resize(function() {
  
  		  setupPorts(viewport, mouseport);
  		  for (var i=0; i<layer.length; i++) {
  		    setupLayer(layer, i, mouseport);
        }
  		});
  
  
  	});
  };
  
  // END OF PLUGIN DEFINITION **********************************************************************
  
  // PLUGIN DEFAULTS
  
  jQuery.fn.jparallax.settings = {
  	mouseResponse:		    true,						// Sets mouse response
  	mouseActiveOutside:		false,					// Makes mouse affect layers from outside of the mouseport.
  	triggerResponse:	    true,					  // Sets trigger response
    triggerExposesEdges:  false,          // Sets whether the trigger pulls layer edges into view in trying to centre layer content.
  	xparallax:				    true,						// Sets directions to move in
  	yparallax:				    true,						//
  	xorigin:					    0.5,				    // Sets default alignment - only comes into play when travel is not 1
  	yorigin:					    0.5,				    //
  	xtravel:              1,              // Factor by which travel is amplified
  	ytravel:              1,              //
  	takeoverFactor:		    0.65,						// Sets rate of decay curve for catching up with target mouse position
  	takeoverThresh:		    0.002,					// Sets the distance within which virtualmouse is considered to be on target, as a multiple of mouseport width.
  	frameDuration:        25							// In milliseconds
  };
  
  // RUN
  
  initOrigin(jQuery.fn.jparallax.settings);
  
  jQuery(function() {
  
  });
  
  
  // END CLOSURE
  
  })(jQuery);
  