/*/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

* =====================================================================================================
* Sooper Dooper Mooseec Troubadoor - a super versatile web media interface
* by designerZen (c) Zenon Olenski 2008-2010.
* www.designerzen.com | designerzen@gmail.com
* =====================================================================================================

// DESCRIPTION :
// This Class simply adds a series of functions to javascript that asynchronously load
// the request into the the div with the id #dynamic.
//
// This is using jQuery's ajax method, history lugin and input plugin for forms
//
// The music player can be controlled with the following functions :
//
// -----------------------------------------------------------------------------------------------------
// PUBLIC METHODS :
//
// playItem( 'filename' );					- play a media item
// loadFeed( 'filename' );					- load a new RSS / IPTC feed
// if (!isSwfLoading) getFlashMovie().showLoadAnimation();	- show the preloader
// if (!isSwfLoading) getFlashMovie().hideLoadAnimation();	- hide the peloader
// if (!isSwfLoading) getFlashMovie().gotoCategory();		- update the main titles (unused)
//
// Flash controls - Access like : if (!isSwfLoading) getFlashMovie().method_name( arguments );
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/

// INTERNAL VARIABLES : DO NOT TOUCH! ============================================================================================
var
isFirstRun = true,			/* has the init completed? or is this a 'back' event? */
isSwfLoading = true,			/* is the SWF loading still? */
isHtmlLoading = true,			/* lock check so that only one page can be requested at a time... */
isJsLoading = true,
useFlash = true,
noPlaybackEngine = false,
dependencyLoadCounter = 0,
lastUrl = '',				/* last visited URL registered from a click event */
lastSection = 'Home',			/* last visited section */
scrollToHash = '',			/* last hashed sub section */
currentlyPlaying = '',			/* song that musicplayer has requested */
attemptedPlay = '',			/* song clicked before swf loaded! */
pageTitle = '';				/* the new title from the freshly ajaxed html */

// CONSTANTS : DO NOT TOUCH! =====================================================================================================
var
BASE_URL,					/* the url hostname */
AJAX_QUERY,					/* this is the mask for which anchors to activate */
SWF_JS_REFERENCE,				/* this is a way for JS to call the swf ID */
MINIMUM_FLASH_PLAYER = 9,			/* */
MIME_TYPES = new Array('.mp3','.flv','.mp4'),	/* */
GOOGLE_TRACKER;					/* google tracker global */

var REGEX_ILLEGAL_CHARS	 = /_|-|{|}|[\u0021-\u002f\u003a-\u0040\u005b-\u005e\u0060\u007b-\u007e]/g;

// INTERNAL CONSTANTS : DO NOT TOUCH! ============================================================================================
/* these are all jQuery objects cached as vars so that we don't have to keep evaluating them during runtime */
var
ELEMENT_COMMENTS_FORM,			/* comment form DIV */
ELEMENT_COMMENTS_STATUS,		/* comment form status DIV */
ELEMENT_DYNAMIC_CONTENT,		/* content DIV to be filled with dynamic content */
ELEMENT_LOADER,				/* DIV to append loader onto! */
ELEMENT_SWF,				/* SWF DIV */
ELEMENT_HTML5_AUDIO,
ELEMENT_HTML5_VIDEO;

/* to ensure compatability with other JS libs, I've used this shorthand $jQ, first we poll to see if JQuery exists... */
if (jQuery) var $jQ = jQuery.noConflict();
else alert("You MUST inlcude jQuery the library");


/*================== FLASH MEDIA CONTROL =================================================================================================================*/
////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC :
function playMedia( filepath, artist, title, cover )
{
	if (useFlash)
	{
		if (isSwfLoading)
		{
		      attemptedPlay = filepath;
		}else{
		      attemptedPlay = '';
		      getFlashMovie().playItem( filepath, artist, title, cover );
		}
	}else{
		playJSMedia( filepath, artist, title, cover );
	}
};

function playJSMedia( filepath, artist, title, cover )
{
	// determine file extension and compare against database :
	// now change the source of the appropriatoe player and call it!
	if (true)
	{
		ELEMENT_HTML5_VIDEO.stop();
		ELEMENT_HTML5_AUDIO.addClass( 'now-playing' ).attr( 'src', filepath );//.play();
	}else{
		ELEMENT_HTML5_AUDIO.stop();
		ELEMENT_HTML5_VIDEO.addClass( 'now-playing' ).attr( 'src', filepath );//.play();.currentTime = 0;
	}
	// $("#bandpress-audio-player")[0].pause(); $("#audio-player")[0].play();
};

////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC :
function pause(){ if (!isSwfLoading) getFlashMovie().pause(); };
function unpause(){	if (!isSwfLoading) getFlashMovie().unpause(); };
function togglePause(){	if (!isSwfLoading) getFlashMovie().togglePause(); };

////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC :
function next(){ if (!isSwfLoading) getFlashMovie().next(); };
function last(){ if (!isSwfLoading) getFlashMovie().last(); };

////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC :
function loadFeed( filepath ){	if (!isSwfLoading && filepath.length) getFlashMovie().loadFeed( filepath, true ); };

////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC :  show preloader & trigger flash animation ( pass in url data )
function showLoader(urlFragment)
{
	if (!isSwfLoading) getFlashMovie().showLoadAnimation( urlFragment );
	//if (!isSwfLoading)getFlashMovie().flash( function() {	this.showLoadAnimation( urlFragment ); } );
};

////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC :  remove the preloader & stop flash animation
function hideLoader(){
	if (!isSwfLoading) getFlashMovie().hideLoadAnimation();
	//if (!isSwfLoading) getFlashMovie().flash( function() {	this.hideLoadAnimation(); } );
};

////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC :
function sendKeypress( keyCode ){ if (!isSwfLoading) getFlashMovie().onJSKeypressed( keyCode );	};

/*================== FLASH! AAAHHHH =================================================================================================================*/

////////////////////////////////////////////////////////////////////////////////////////////////
// Get the SWF element as a JS reference
////////////////////////////////////////////////////////////////////////////////////////////////
function getFlashMovie()
{
	if (!SWF_JS_REFERENCE)
	{
		var isIE = navigator.appName.indexOf("Microsoft") != -1;
		SWF_JS_REFERENCE = (isIE) ? window[SWF_ID] : document[SWF_ID];
	}
	return SWF_JS_REFERENCE;
};

/* == Zens Media Plugin Part I == */
(function( $ )
{

	/* Default vars here :
	var methods =
	{
		init : function( options ) { // THIS },
		show : function( ) { // IS   },
		hide : function( ) { // GOOD },
		addPlayer : function( content ) { // !!! }

		playItem: function( options ){},
		play: function(){},
		pause: function(){},
		togglePause: function(){},
		next: function(){},
		last: function(){},
		playMedia: function(){},
		loadFeed: function(){},
		showLoader: function(){},
		hideLoader: function(){},
		sendKeypress: function(){}
	};

	$.fn.controlMedia = function( method )
	{

	    // Method calling logic
	    if ( methods[method] ) {
		return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
	    } else if ( typeof method === 'object' || ! method ) {
		return methods.init.apply( this, arguments );
	    } else {
		$.error( 'Method ' +  method + ' does not exist on jQuery.controlMedia' );
	    }
	};*/
	////////////////////////////////////////////////////////////////////////////////////////////////
	// PUBLIC : ADD MEDIA PLAYER TO JQUERY ELEMENT
	// Replace our named div with a swf :D = The id must match what we call later to control it
	// the initial flashVars carry the RSS feed / page info so that if we permalink to a hashed page,
	// or we load from a fresh category, the swf realises we arent @home
	////////////////////////////////////////////////////////////////////////////////////////////////
	$.fn.addPlayer = function( options )
	{

	      if ( $jQ.flash.available )
	      {
		      var settings =
		      {
			'swf'    		: SWF_URL,
			'id' 			: SWF_ID,
			'height' 		: SWF_HEIGHT,
			'width'			: SWF_WIDTH,
			'hasVersion'		: MINIMUM_FLASH_PLAYER,
			'express'		: getThemeDir() + "flash/expressinstall.swf",
			'feed'			: PLAYER_DEFAULT_RSS_FEED,
			'crossfade'		: PLAYER_CROSSFADE_CURVE,
			'crossfadeDuration' 	: PLAYER_CROSSFADE_DURATION,
			'autostart'		: PLAYER_AUTOSTART,
			'seamless'		: PLAYER_CROSSFADE,
			'bufferLength'		: PLAYER_BUFFER_DURATION
		      };

		      // If options exist, lets merge them with our default settings
		      if ( options ) $.extend( settings, options );

		      // TODO: we should extract the relevant FEED and pass it to flash here
		      var cssColour = this.css("background-color");
		      var wmode = ( cssColour === 'transparent' ) ? 'transparent' : 'window';
		      var bgColour = ( cssColour === 'transparent' ) ? '#FFFFFF' : rgb2hex( cssColour );

		      // embed flash now after checking version number!
		      this.addClass("flash-embedded "+CSS_NOW_LOADING ).flash
		      (
			      {
				      swf: settings.swf,
				      height: settings.height,
				      width: settings.width,
				      bgcolor: bgColour,
				      id:settings.id,
				      allowFullScreen: true,
				      wmode: wmode,
				      expressInstaller: settings.express,
				      hasVersion: settings.hasVersion,
				      hasVersionFail: onSWFFailedToLoad,
				      flashvars:
				      {
					      feed:settings.feed,
					      seamless:settings.crossfade,
					      autostart:settings.autostart,
					      crossfade_duration:settings.crossfadeDuration,
					      crossfade_curve:settings.crossfade,
					      buffer_duration:settings.bufferLength,
					      image:PLAYER_ARTWORK,
					      title:PLAYER_TITLE
				      }
			      }
		      );
	      }else{
		      // flash was not installed on the users machine ;(
		      useFlash = false;
		      isSwfLoading = true;
		      onFlashDisabled();

		      // can we fallback to the javascript mp3 engine...?
		      if (!!document.createElement('audio').canPlayType)
		      {
			      ELEMENT_HTML5_AUDIO = $jQ("<audio name='audio-player' id='bandpress-audio-player' src=''>test html5 stylee</audio>");
			      ELEMENT_HTML5_VIDEO = $jQ("<video name='video-player' id='bandpress-video-player' src=''></video>");
			      this.append( ELEMENT_HTML5_AUDIO );
		      }else{
			      // EPIC FAIL
			      onUltimateMediaEngineFail();
		      }
	      }
	};

	////////////////////////////////////////////////////////////////////////////////////////////////
	// If flash player is disabled...Add some error messages and such
	////////////////////////////////////////////////////////////////////////////////////////////////
	function onFlashDisabled()
	{
		var userAgent = navigator.userAgent.toLowerCase();
		// this may need to be updated to match iPhone & iPod for OS4
		var blueLego = /ipad/.test(userAgent) || /iphone/.test(userAgent) || /ipod/.test(userAgent);
		// probably a stupid apple user...
		if (blueLego) ELEMENT_SWF.html( "<div style='background-color:#FFF;border:1px solid #CCC; text-align:center;'><a href='http://get.adobe.com/flashplayer/' target='_blank'><img src='blueLego.gif' width='48' height='48'/></a></div>" );
	};

	////////////////////////////////////////////////////////////////////////////////////////////////
	// If the flash player is an older, unsupported version, what do we do?
	// Add some error messages and such
	////////////////////////////////////////////////////////////////////////////////////////////////
	function onSWFFailedToLoad(options)
	{
		ELEMENT_SWF.removeClass( CSS_NOW_LOADING ).html( SWF_ERROR_FLASH_DISABLED );
		isSwfLoading = true;
		//console.dir(options); // look at all the useful goodies

		return true; // would have let the expressInstaller document be used
	};

	////////////////////////////////////////////////////////////////////////////////////////////////
	// No Flash or HTML5 o_O :: FAIL FAIL FAIL!
	////////////////////////////////////////////////////////////////////////////////////////////////
	function onUltimateMediaEngineFail()
	{
		noPlaybackEngine = true;
		ELEMENT_SWF.html( SWF_ERROR_FLASH_DISABLED );
	};

	////////////////////////////////////////////////////////////////////////////////////////////////
	// Replace any html divs with the class swf-embed.
	// The attributes of the div determines the SWF parameters
	// Styling for width and height is done via CSS
	// It also appends a link to a page to show it fullscreen.
	// <a class='embedflash' id='flashID' title='swfURL.swf' href='swfURL.swf' style='width:50px; height:25px;' name='anchorSection' >{flashvars=true&fd='yes'}</a>
	// bgcolor: SWF_BACKGROUND_COLOUR,
	// Also, we want inline flash to play only once clicked and *not* straight away
	////////////////////////////////////////////////////////////////////////////////////////////////
	$.fn.injectInlineFlash = function()
	{
		// there's no need to do $(this) because "this" is already a jquery object
		// $(this) would be the same as $($('#element'));
		$jQ( "a.flash-external,a[href*='.swf']", this ).each
		(
		    function (index, domEle)
		    {
				var
				cssObj = {}, newHtml='',
				anchorElement = $(this), caption = anchorElement.text(),
				mediaClasses = 'click-to-show-flash flash-inline',
				image = anchorElement.find("img");

				// check to see if this contains an image, and if so, use those dimensions instead!
				if (image.length)
				{
					  // super obscure .outerhtml() in jQuery :/
					  var
					  imageHtml = image.removeClass().clone().wrapAll("<div/>").parent().html(),
					  imageWidth = imageHtml.match(/width="(\d+)"/),
					  imageHeight = imageHtml.match(/height="(\d+)"/);// .exec(response)[1]
					  cssObj.width 	= imageWidth[1]+'px';
					  cssObj.height = imageHeight[1]+'px';
					  cssObj.backgroundImage = 'url('+image.attr( 'src' )+')';
					  //mediaClasses += image.attr('class');
					  newHtml = imageHtml+'<span class="click-to-activate"></span>';
				}else{
					  // set width and height of the enclosure and fill with something
					  // ^[+-]?[0-9]+\.?([0-9]+)?(px|em|ex|%|in|cm|mm|pt|pc)$
					  var
					  anchorHtml = anchorElement.removeClass().clone().wrapAll("<div/>").parent().html(),
					  anchorWidth = anchorHtml.match(/width:\s*(\d+)(px|em|ex|%|in|cm|mm|pt|pc)/),
					  anchorHeight = anchorHtml.match(/height:\s*(\d+)px/);
					  //oldWidth = anchorElement.width(), oldHeight = anchorElement.height();
					  cssObj.width = (anchorWidth[0] < 50) ? 568+'px' : anchorWidth[0]+'px';
					  cssObj.height = (anchorHeight[0] < 50) ? 320+'px' : anchorHeight[0]+'px';
				}

				cssObj.display = 'block';
				cssObj.position = 'relative';

				if (caption.length) newHtml += "<span class='swf-caption'>"+caption +"</span>"

				anchorElement
				.attr('alt','Click to show flash')
				.html(newHtml)
				.addClass(mediaClasses)
				.css( cssObj )
				.click( onInlineFlashClicked );
				// append ( view in popup, popup-php?url=flash.swf&height=100&width=100 );
		    }
		);
		return this;
	};

	function onInlineFlashClicked( event )
	{
		var anchorElement = $jQ(this);

		// remove former classes and this click handler...
		anchorElement.removeClass('click-to-show-flash').unbind("click");
		// use title unless href is available!
		var sourceURL = (this.href.length >4) ? this.href : this.title;
		// get an ID from the id (if none set use timestamp)
		var flashID = (this.id) ? this.id : ( ""+new Date().getTime() + Math.random() ).slice(0, 10);
		var wmode, bgColour;
		if ( anchorElement.css("background-color") === 'transparent' )
		{
			wmode = 'transparent';    // set transparency
			bgColour ='#FFFFFF';
		}else{
			wmode = 'window';		// opaque is buggy!
			bgColour = rgb2hex( anchorElement.css("background-color") );// fetch background colour for swf
		}

		var swfEmbedCode = $jQ.flash.create(
			{
			  bgcolor: bgColour,
			  swf: sourceURL,
			  width: SWF_WIDTH,
			  height: SWF_HEIGHT,
			  allowFullScreen: true,
			  src: sourceURL,
			  id:"swf_"+ flashID,
			  wmode: wmode,
			  menu: false
			}
		);

		var closeButton = $jQ( "<a class='close-flash' href='#swf_parent_" +flashID+ "' title='close'>close</a>" );
		var replacement = $jQ( "<div style='"+anchorElement.attr("style")+"' id='swf_parent_" +flashID+ "' class='flash-embedded flash-inline'>"+"</div>" ).html( swfEmbedCode ).append( closeButton );

		// now add a close button to the bottom : that removes the flash element only...
		closeButton.click( function( event )
		{
			anchorElement.addClass('click-to-show-flash').click( onInlineFlashClicked );	// and finally rerun the add handler script!
			replacement.replaceWith( anchorElement );//
			event.preventDefault();
		} );

		// attach embed code
		// if ( showEmbed ) $jQ( this ).append( <code> '+ flashembed.getHTML({src: 'my_object.swf'}, { param1: 'foobar', param2: 'baz' }) +' </code> );

		// now inject into DOM
		anchorElement.replaceWith( innerShiv(replacement, false) );

		event.preventDefault();
	};
})( jQuery );

////////////////////////////////////////////////////////////////////////////////////////////////
//  *.  CALLED BY FLASH ===
// 	If the flash player for some reason cannot listen to JS events, trigger here
////////////////////////////////////////////////////////////////////////////////////////////////
function onSWFFailedToCommunicate()
{
	ELEMENT_SWF.removeClass( CSS_NOW_LOADING ).addClass( '' ).after( '' );
	alert("Your Browser sadly does not support JS like a good browser should. Have you ever considered Firefox?");
};
////////////////////////////////////////////////////////////////////////////////////////////////
//  *.  CALLED BY FLASH ===
//	onSWFLoaded function
//	* when swf has loaded the AS3 passes this event back via the ExternalInterface to show
// 	that loading is done. We need to monitor this as otherwise, we cannot call
//	the swf via javascript without errors!
////////////////////////////////////////////////////////////////////////////////////////////////
function onSWFLoaded()
{
	ELEMENT_SWF.removeClass( CSS_NOW_LOADING );
	isSwfLoading = false;
	// user has already requested a song to play but swf hadn't downloaded yet, so now play!
	if ( attemptedPlay.length > 4 ) playMedia( attemptedPlay );
};


/*================== INTERNAL =================================================================================================================*/

////////////////////////////////////////////////////////////////////////////////////////////////
// INITIALISATION : Fetch VARIABLES & set CONSTANTS
////////////////////////////////////////////////////////////////////////////////////////////////
function init(  )
{
	BASE_URL = extractBaseLocation();				// set our root urls
	SWF_URL = getThemeDir() + SWF_URL;				// set the url of the swf
	AJAX_QUERY = "a[href*="+BASE_URL+"]:not([target^='_blank'])";	// add any exceptions to our click hijacking rules (such as admin sections etc)	& remove internal links that target _blank
	for (var e=0; e<AJAX_EXCEPTIONS.length; e++) AJAX_QUERY += ":not("+AJAX_EXCEPTIONS[e]+")" ;// set the repeated click catching
	loadDependencies();
};

function prefetchDOMElements()
{
	ELEMENT_DYNAMIC_CONTENT = $jQ( DIV_DYNAMIC );			// save ref to dynamic content
	ELEMENT_LOADER = $jQ( "body" );					// svae ref to loader DIV
	ELEMENT_SWF = $jQ( SWF_DIV );					// Our Swf's DIV
};

function loadDependencies()
{
	// Introspectively call until all dependencies have been met :D
	if ( dependencyLoadCounter < DEPENDENCIES.length ) $jQ.getScript( getThemeDir() + DEPENDENCIES[ dependencyLoadCounter++ ] ,function(){ loadDependencies(); });
	else onDependenciesLoaded();
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//====================================================- EVENTS FOR EVERYTHING -=====================================================///
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////
// 0. PRE-Process all our of variables and set our sources before page load
////////////////////////////////////////////////////////////////////////////////////////////////
init();

////////////////////////////////////////////////////////////////////////////////////////////////
// 1. GO! DOM Loaded -> Begin Javascript domination >:)
////////////////////////////////////////////////////////////////////////////////////////////////
$jQ(document).ready(onDOMLoaded);				// HiJack on Load
window.onbeforeunload = onLeavingSite;				// HiJack on Exit

////////////////////////////////////////////////////////////////////////////////////////////////
// 2. DOM has downloaded locally! This only ever gets called the ONCE
// NB. I have optimised this order for smooth loading
////////////////////////////////////////////////////////////////////////////////////////////////
function onDOMLoaded()
{
  	isHtmlLoading = false;						// set our load checker to false
	prefetchDOMElements();
	onDocumentReady();
};

////////////////////////////////////////////////////////////////////////////////////////////////
// 2. Javascript dependencies have been met with no issue... proceed!
////////////////////////////////////////////////////////////////////////////////////////////////
function onDependenciesLoaded()
{
	isJsLoading = false;
	onDocumentReady();
};

function onDocumentReady()
{
	if ( isHtmlLoading||isJsLoading ) return;

	// alert( getFeedURi() );
	ELEMENT_SWF.addPlayer( '' );	// add the flash (with our known parameters...)

	// hijack media files and create our custom javascript links!
	$jQ('body')
	.hijackMediaLinks( MIME_TYPES )
	.hijackInternalLinks( AJAX_QUERY )
	.injectInlineFlash();						// hijack ALL INTERNAL links (except wordpress admin shit / media files)

	addAjaxFormHandlers( DIV_COMMENT_FORM );			// hijack INPUT button for comment submissions
	addSearchFormHandler();						// hijack Search Input field for search submissions

	// only performed once!
	addKeyboardHandlers();						// hijack some keyboard shortcuts

	themeSpecificInitialisation();					// add some theme specific sparkle...

	// Initialize history plugin. ( callback function as arg )
	$jQ.history.init( onHtmlRequested );				// The callback is called at once by present location.hash.
};



////////////////////////////////////////////////////////////////////////////////////////////////
//  4. onHtmlRequested function
// 	This function is called :
//	* onload if the permalink is a hashed one
// 	* after calling loadURL()
// 	* after pushing "Go Back" button of a browser
////////////////////////////////////////////////////////////////////////////////////////////////
function onHtmlRequested( urlFragment  )
{
      // as this is the first time we
      // do stuff that loads page content based on hash variable
      // hash doesn't contain the first # character.
      // check to see if this is a result of the user hiting BACK button
      // if it is (not isFirstRun), force it home anyway!!!!
      if (isFirstRun) googleTrackInit();

      if( (urlFragment) || (!isFirstRun) )
      {
		lastUrl = urlFragment;
		if ( urlFragment.indexOf( '/' ) == 0 )
		{
			// try to AJAX this page in
			// if ((lastUrl == urlFragment)&&(force!=true)) return;  // looks like we are reloading the current page...
			// show the preloader
			onHtmlLoadStart( urlFragment );
			// restore ajax loaded state with the AJAX call to the url from above

			// FIX : firefox crashes unless you remove inline flash :(
			$jQ( '.flash-inline', DIV_DYNAMIC ).flash().remove();
			$jQ.ajax({
				  type: 'GET',
				  url: urlFragment,
				  cache: false,
				  /*beforeSend: onHtmlLoadStart,*/

				  dataType:"html",
				  dataFilter:onHtmlReceived,
				  success: onHtmlLoaded,
				  error: onHtmlFailedToLoad
			});

			 // page has updated!
			 isFirstRun = false;
		}else{
			  scrollViewPortToDiv( DIV_DYNAMIC, '#'+urlFragment ); 	// Navigate to a hash on the page
		}
      } else {
		// start page(no hashing)/ actual permalink / init
      }

};

////////////////////////////////////////////////////////////////////////////////////////////////
// 5. AJAX Call has been reqested...
////////////////////////////////////////////////////////////////////////////////////////////////
function onHtmlLoadStart( urlFragment )
{
	isHtmlLoading = true;

	if (scrollToHash.length < 1)
	{
		scrollViewPortTo( 0 );					// scroll viewport to the top (if no hash to jump to)
		updateDocumentTitle( TEXT_LOADING );			// update title to loading...
	}else{
		scrollViewPortToDiv( DIV_DYNAMIC ,'');			//
		updateDocumentTitle( TEXT_LOADING + scrollToHash );	// if there is a hash to scroll to append it onto the title :D
	}

	// user override function to style divs based on events :
	updateThemeCss( BASE_URL+urlFragment );				// update the css to show what has been clicked

	showLoader( urlFragment );					// show feedback
	hideDynamicContent();
};

////////////////////////////////////////////////////////////////////////////////////////////////
// So now the element we wanted to hide is display:none. so it would be prudent to stick some
// arbitrary loader div to be replaced when loaded...
////////////////////////////////////////////////////////////////////////////////////////////////
function onHtmlHidden()
{

};

////////////////////////////////////////////////////////////////////////////////////////////////
// 6. We can now pre-process the html received from the call...
////////////////////////////////////////////////////////////////////////////////////////////////
function onHtmlReceived( response, dataType )
{
	// find the classes belonging to our body element and transfer them to the new element :)
	var bodyClassess = (/<body class=\"(.*?)\">/m).exec(response)[1];
	$jQ("body").attr("class",bodyClassess);// = bodyClassess;

	// find out the page title from the response...
	var newPageTitle = (/<title>(.*?)<\/title>/m).exec(response)[1];
	if (newPageTitle.length > 1) pageTitle = newPageTitle;
	else pageTitle='';

	return $jQ( DIV_DYNAMIC, response ).injectInlineFlash()
	.hijackMediaLinks( MIME_TYPES )
	.hijackInternalLinks( AJAX_QUERY );
};

////////////////////////////////////////////////////////////////////////////////////////////////
// 6. Called when the new html has loaded and has been injected back into the DOM.
////////////////////////////////////////////////////////////////////////////////////////////////
function onHtmlLoaded( response, status, xhr )
{
	if (status == "error")
	{
		  // Page not loaded!
		  onHtmlFailedToLoad();
	}else{
		  // Inject new data into element space after pasring as object:
		  ELEMENT_DYNAMIC_CONTENT.html( innerShiv(response, false) )

		  // update the new html with input events
		  addAjaxFormHandlers( DIV_COMMENT_FORM );					// add relevant comment hooks
		  addSearchFormHandler( DIV_DYNAMIC );						// hijack search field

		  themeSpecificInitialisation( DIV_DYNAMIC );					// add theme specific css

		  // Work out the new title here... if pageTitle has been read from the ajax request...
		  if (pageTitle.length) setDocumentTitle( pageTitle );
		  else updateDocumentTitle( (lastSection.length) ? lastSection : scrollToHash );// update the title of this page

		  hideLoader();
		  isHtmlLoading = false;

		  revealDynamicContent();
	}

};

////////////////////////////////////////////////////////////////////////////////////////////////
// 6. Called when the new html could not be loaded from the server
////////////////////////////////////////////////////////////////////////////////////////////////
function onHtmlFailedToLoad(XMLHttpRequest, textStatus, errorThrown)
{
	isHtmlLoading = false;
	// add error message to dynamic element! or alert box?
	revealDynamicContent();
	ELEMENT_DYNAMIC_CONTENT.prepend("<div class='error error-404'>"+ERROR_404+"</div>");
};

////////////////////////////////////////////////////////////////////////////////////////////////
// 6. Called when the new html has finished animating the content onto the screen
////////////////////////////////////////////////////////////////////////////////////////////////
function onHtmlVisible()
{
	if (scrollToHash.length > 0) scrollViewPortToDiv( DIV_DYNAMIC, scrollToHash );	// scroll to hash (if set) : <span id="more-82"/> #comments
	googleTrack( lastUrl );								// var breadcrumb = '/my/virtual/url';	// convertToPermalink();
};




/* == User Events == */

////////////////////////////////////////////////////////////////////////////////////////////////
// KEYPRESS > User has pressed one of our shortcut keys!
////////////////////////////////////////////////////////////////////////////////////////////////
function onKeyPress( event )
{
	// Kill keypress events if focus is on input boxes
	var elem = $jQ(event.target);
	if (elem.is("input") || elem.is("textarea") || elem.is("select")) return;

	//alert( 'which : '+ event.which + ' keycode : '+event.keyCode);

	// Figure out the best way to read the event keycode...
	var keyPressed = event.which;						// try the documented way...
	if ( keyPressed < 1 ) keyPressed = event.keyCode;	// fail... lets use the other way

	// Special cases. don't ask me why, i simply cant tell you.
	if ((keyPressed == 61)||(keyPressed== 43)) keyPressed = 187;		// +
	else if ((keyPressed == 95)||(keyPressed== 45)) keyPressed = 189;	// -
	sendKeypress( keyPressed );
};

////////////////////////////////////////////////////////////////////////////////////////////////
// EXIT > If the user navigates away from the site - instruct flash to bookmark the data!!!
////////////////////////////////////////////////////////////////////////////////////////////////
function onLeavingSite()
{
	// The swf has loaded...
	if( !isSwfLoading )
	{
		getFlashMovie().saveUserData();		// SWF CALL === save data
	}
	// If you wanna do something else fancy add it here...
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//======================================================- DOM Manipulation -========================================================///
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////
// Hide and Show the Dynamic Element
////////////////////////////////////////////////////////////////////////////////////////////////
function hideDynamicContent()
{
	ELEMENT_LOADER.addClass( CSS_NOW_LOADING );
	// animate element  //.is(':animated')
	//ELEMENT_DYNAMIC_CONTENT.fadeOut("fast", onHtmlHidden);"height": "hide",		// animate out current info .stop().
	//ELEMENT_DYNAMIC_CONTENT.animate( { height:"hide" }, { duration: 250, queue: false, complete:onHtmlHidden }, "swing" );
	ELEMENT_DYNAMIC_CONTENT.animate( {  "opacity": 0 }, { duration: 450, queue: true, complete:onHtmlHidden }, "swing" );
	//ELEMENT_DYNAMIC_CONTENT.slideUp( 750, onHtmlHidden );
};
function revealDynamicContent()
{
	//ELEMENT_DYNAMIC_CONTENT.fadeIn("fast",onHtmlVisible);
	// hide the image & animate the new data in the dynamic div..hide() "height":"show",
	//ELEMENT_DYNAMIC_CONTENT.slideDown( 1000,onHtmlVisible ); //queue: false,
	ELEMENT_DYNAMIC_CONTENT.stop( true, true ).animate( {"opacity": 1 }, { duration: 550, queue: false, complete:onHtmlVisible }, "linear" );
	ELEMENT_LOADER.removeClass( CSS_NOW_LOADING );
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//======================================================- AJAX BEGINS HERE -========================================================///
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC : Load a URL into our dynamic div and track it
// ARGS: LoadURL( url to load, hashed anchor to scroll to)
// DESC: Load any html into our dynamic DIV (set above)
// 	 We talk to this from html anchors and also from
// 	 actionscript (to AJAX jump to sections)
////////////////////////////////////////////////////////////////////////////////////////////////
function loadURL( url, hashID )
{
	// TODO :
	// check to see if this is simply a hash link AKA #comments if ( this.href.indexOf( '#' ) == 0 )
	// first thing we need to do is to check if we are just calling a hash on our current page...
	// so we compare the new url with the previously saved url, and if it is the same but with
	// a named anchor, we skip loading and go straight to scrollToViewport...
	// also if it simple a named hashed anchor such as #top we jump straight to it!
	// scrollViewPortToDiv( DIV_DYNAMIC+" span[id^='more-']" );
	// return;

	// security check! make sure we aren't trying to load in a media file or js or anything...
        //var invalidFilename = url.indexOf(".mp3");
	//if (invalidFilename > 0) { return false; }

	// save the anchored hash for scrolling to later on!
	scrollToHash = hashID;

	// create our new hash to append....
	url = purgeHash( url );				// (for anchored sections - else breaks jQuery load...)
	url = url.substring( BASE_URL.length );		// remove our http://www..com
	url = url.replace(/^.*#/, '');			// keep everything before the hash

	//url += " " + DIV_DYNAMIC ;
	$jQ.history.load( url );			// now send that hash to the history mapper
};

////////////////////////////////////////////////////////////////////////////////////////////////
// Allow USER input to control the playing media by using the keyboard
////////////////////////////////////////////////////////////////////////////////////////////////
function addKeyboardHandlers()
{
	// Ok, there seems to be some bugs when looking for keypress on IE and Safari...
	if ( $jQ.browser.mozilla ) $jQ( document ).keypress( onKeyPress );
	else $jQ( document ).keydown( onKeyPress );
};



(function( $ )
{
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Hijack the current links and redirect them to our new AJAX load requester & update title and hash
	// Apply click handlers to anchors pointing internally
	// ( as we only want this occuring to internal links to wordpress, so ignore att, blank )
	////////////////////////////////////////////////////////////////////////////////////////////////
	$.fn.hijackInternalLinks = function( query )
	{
		$( query, this ).each(function() {	$( this ).click(onInternalLinkClicked);		});
		return this;
	};

	////////////////////////////////////////////////////////////////////////////////////////////////
	// An Media Link has been created and pressed!
	////////////////////////////////////////////////////////////////////////////////////////////////
	function onInternalLinkClicked( event )
	{
		// emulate :visited
		$jQ( this ).addClass( CSS_VISITED );

		// save the title for display later
		lastSection = this.title;
		// find where the anchor originally linked to
		loadURL( this.href, this.hash );
		// if ( lastSection.length < 1 ) lastSection = this.text;

		event.preventDefault();							// stop normal link clicking
		return false;
	};

})( jQuery );


(function( $ )
{
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Hijack all links pointing to mp3 files anywhere on the internets.
	// This sends info to the embedded flash file,
	// adds a graphical link play button, and also adds a download link if set in settings
	// This action plugin converts any mimetypes within that specific element into media player links
	////////////////////////////////////////////////////////////////////////////////////////////////
	$.fn.hijackMediaLinks = function(  mimeTypes, classFilter, nodeParser, queryParser )
	{
		var mimeQuery = "";
		queryParser = ( queryParser == null||undefined) ? 'href$=' : queryParser+"=";
		classFilter = ( classFilter == null||undefined ) ? '' : "."+classFilter;
		if ( nodeParser == null||undefined) nodeParser = 'a';		// default node to parse is anchors, can be changed to audio

		// check to see if the mimeType argument is an array or a simple string
		if ( isArray(mimeTypes) )
		{
			  // loop through the array and append mimetype filters
			  mimeQuery += nodeParser + "[" + queryParser + "'"+mimeTypes[0]+"']"
			  for (var m=1; m<mimeTypes.length; ++m) mimeQuery += ",["+queryParser+"'"+mimeTypes[m]+"']" ;// set the repeated click catching
			  mimeQuery += classFilter;
			  //alert( mimeQuery );
		}else{
			  mimeQuery = nodeParser + "[" + queryParser + "'"+mimeTypes+"']"+classFilter;
		}
		// loop through all mimetype links and add corresponding graphics :
		$jQ( mimeQuery , this ).each(
			function (index, domEle)
			{
				// update the current link with new data
				var anchorElement = $( this ), mediaTitle = this.title, mediaName = this.name;
				if ( !mediaTitle.length ) mediaTitle = anchorElement.text( ) ;
				//anchorElement; // $( this ).parent().prev().text();

				// check to see if this link contains an image, and if so, use that as the cover-art :@)
				var image = anchorElement.find("img");
				if ( image.length )
				{
					mediaName = image.attr( 'src' );
					image.remove();
				}

				var mediaClasses = CSS_BUTTON+' '+CSS_LISTEN;
				if ( this.href == currentlyPlaying ) mediaClasses += ' '+CSS_PLAYING;// change the link style here to contain graphics!

				anchorElement
				.html( LABEL_LISTEN + ' '+mediaTitle )
				.addClass( mediaClasses )				// change the link style here to contain graphics!
				.attr({
					'title' : mediaTitle,
					'alt' : LABEL_LISTEN,
					'name' : mediaName
				})
				.click( onMediaItemClicked )					// apply click handlers to anchors leading to media files & send to standard play media proxy;
				.after( createDownloadLink( this.href, mediaTitle ) );
			}
		);

		addSoundCloudLinks(this);
		// HTML5 : allow them to play in hidden player instead... check for <audio> nodes and translate them!
		//if (!!document.createElement('audio').canPlayType) addHTML5AudioFactoryClickHandlers( elementFilter );

		return this;
	};
	////////////////////////////////////////////////////////////////////////////////////////////////
	// An Media Link has been created and pressed!
	////////////////////////////////////////////////////////////////////////////////////////////////
	function onMediaItemClicked( event )
	{
		//this.addClass( CSS_REPLAY );						// add VISITED class ( emulates :visited )

		// extract some pertinent information (we should check here the name of the element!!!)
		var
		URL = this.href,
		title = this.title,
		artist = this.hash,
		coverart = this.name; // find blog art

		playMedia( URL , title , artist, coverart );		// send this to flash to play :D

		event.preventDefault();							// stop normal link clicking
		return false;
	};
	// Simply strip back the URL and set the href to send data to the player$.fn. =
	function addSoundCloudLinks( filter )
	{
		// [not:"dropbox/"]
		$jQ( "a[href^='http://soundcloud.com/']", filter )  	// http://soundcloud.com/sixfingerz/a-new-existence  & really we just want everything after last "/"
		.addClass( CSS_BUTTON+' '+CSS_LISTEN )				// change the link style here to contain graphics!
		.attr( 'alt', LABEL_LISTEN )					// set attribute to listen
		.click( onMediaItemClicked );					// apply click handlers to anchors leading to media files & send to standard play media proxy;
	};

	// add <audio> tags too$.fn. =
	function addHTML5Audio()
	{
	  /*
		$jQ( 'audio', elementFilter ).each(
		    function (index, domEle)
		    {
			var songTitle = this.title; // $( this ).parent().prev().text();
			if ( this.href == currentlyPlaying ) $jQ( this ).after( createDownloadLink( this.href, songTitle ) ).replaceWith( createNowPlayingLink(this.href, 'listen') );
			else $jQ( this ).after( createDownloadLink( this.href, songTitle ) ).replaceWith( createListenLink(this.href, 'listen') );
		    }
		);*/
	};

	// Generate the html anchor links for the download button (feel free to modify)
	function createDownloadLink( url, title )
	{
	      return ( SHOW_DOWNLOAD_LINKS ) ? '<a class="'+CSS_BUTTON+' '+CSS_DOWNLOAD+'" href="'+url+'" title="'+title+'" alt="'+LABEL_DOWNLOAD+'">'+LABEL_DOWNLOAD+' '+title+'</a>' : '';
	};

})( jQuery );

/* == Media Events == */



////////////////////////////////////////////////////////////////////////////////////////////////
// 7. FLASH CALLED ===
//	Called when the song is selected via html, and also called by FLASH when it automatically
//	changes tracks in the playlist. The songFilename is the url of the MP3.
////////////////////////////////////////////////////////////////////////////////////////////////
function onMediaItemStart( songFilename )
{
	if (currentlyPlaying == songFilename)
	{
		// may be paused!
	}else{
		currentlyPlaying = songFilename;				// save this for duplicate calls
		googleTrack( songFilename );					// track this song :D
	}
	updateCssNowPlaying( songFilename );		// update the DIV css for that song
	//alert("track playing...");
};

////////////////////////////////////////////////////////////////////////////////////////////////
// 7. FLASH CALLED ===
//	Called when the user clicks pause...
////////////////////////////////////////////////////////////////////////////////////////////////
function onMediaItemPaused( filename )
{
	updateCssNowPaused( filename );
	//alert("track paused...");
	// set document title to *paused*
	updateTitle( '‖', filename );
};

// TODO: add onProgress var icon = ' ▶ ';
function onMediaItemProgress( progressReport )
{
	// change the title....
	setDocumentTitle( progressReport );
	// update internal vars ( for if you want a css frontend... )
};

function onMediaItemStop( filename )
{
	updateCssStopped( filename );
	//alert("track stopped...");
}

////////////////////////////////////////////////////////////////////////////////////////////////
// 7. FLASH CALLED 404 ===
//	Called when the music player cannot find the MP3
////////////////////////////////////////////////////////////////////////////////////////////////
function onMediaItemFailedToPlay( error )
{
	// change the css for that link with an error-class
	alert('Song failed '+error );
};

/*
////////////////////////////////////////////////////////////////////////////////////////////////
// This adds an active class to all
// and removes all classes that aren't active on items
////////////////////////////////////////////////////////////////////////////////////////////////
function updateItemCss( url )
{
	$jQ( "a[href="+url+"]" ).removeClass( CSS_PLAYING );
	$jQ( "a[href="+url+"]" ).addClass( CSS_PLAYING );
};
*/
////////////////////////////////////////////////////////////////////////////////////////////////
// This adds an 'inactive' class to all active songs and removes all 'active'
// classes that aren't active on music files
////////////////////////////////////////////////////////////////////////////////////////////////
function updateCssStopped( url )
{
	//alert('updateCssStopped '+url);
	$jQ( "a[href$=" +url+ "]."+CSS_LISTEN ).removeClass( CSS_PLAYING +' '+ CSS_PAUSED ); 	// strip all media with active classes...
};
////////////////////////////////////////////////////////////////////////////////////////////////
// This adds an 'inactive' class to all active songs and removes all 'active'
// classes that aren't active on music files
////////////////////////////////////////////////////////////////////////////////////////////////
function updateCssNowPlaying( url )
{
	//alert('updateCssNowPlaying '+url);
	$jQ( "a."+CSS_LISTEN ).removeClass( CSS_PLAYING +' '+ CSS_PAUSED ); 	// strip all media with active classes...
	$jQ( "a[href$=" +url+ "]."+CSS_LISTEN ).addClass( CSS_PLAYING );	// and add the active class to the mp3
};

////////////////////////////////////////////////////////////////////////////////////////////////
// This adds an 'active' class to all active songs and removes all 'active'
// classes that aren't active on music files
////////////////////////////////////////////////////////////////////////////////////////////////
function updateCssNowPaused( url )
{
	//alert('updateCssNowPaused '+url);
	//alert( url + " != " + currentlyPlaying);
	// A song must be highlighted...
	if (url != currentlyPlaying) return;
	var currentlyHighlightedLink = $jQ( "a[href$=" +url+ "]."+CSS_LISTEN );		// If the href ENDS in the currentlyPlaying track...
	currentlyHighlightedLink.removeClass( CSS_PLAYING ).addClass( CSS_PAUSED );	// and add the active class to the mp3
};




/*================== BROWSER CONTROL FUNCTIONS ===================*/
////////////////////////////////////////////////////////////////////////////////////////////////
// Swap the current CSS style sheet for a different one specified in the arguments
////////////////////////////////////////////////////////////////////////////////////////////////
function loadNewCSS( cssClass, styleSheetURL )
{
	var selector = 'style[type="text/css"]';
	$( selector ).text('@import url("'+styleSheetURL+'");');
};

////////////////////////////////////////////////////////////////////////////////////////////////
// Set the Window title to something else (feel free to modify) ▶ ▶ ▶ ▶ ▶
////////////////////////////////////////////////////////////////////////////////////////////////
function updateTitle( title, filename )
{
	var safeTitle = extractLocation( purgeExtension(filename) );	// if it is a URL, we only care about the last section!!!
	safeTitle = safeTitle.replace( REGEX_ILLEGAL_CHARS, ' ' );
	updateDocumentTitle( toTitleCase(safeTitle) );
};

////////////////////////////////////////////////////////////////////////////////////////////////
// Set the Window title to something else (feel free to modify)
////////////////////////////////////////////////////////////////////////////////////////////////
function updateDocumentTitle( title )
{
	var divider = ' | ';
	var safeTitle = unescape( title );
	setDocumentTitle( BASE_TITLE+divider+safeTitle );
};

function setDocumentTitle( documentTitle )
{	// A suggested limit for the number of characters in a TITLE is 60.
	//if ( safeTitle.length > 60 )
	//document.title = documentTitle ;
	$jQ(document).attr('title', documentTitle);
};

////////////////////////////////////////////////////////////////////////////////////////////////
// This scrolls the viewport to a named element
////////////////////////////////////////////////////////////////////////////////////////////////
function scrollViewPortToElement( element )
{
	var newYCoord = element.offset().top - SCROLL_OFFSET;
	scrollViewPortTo( newYCoord );
};

////////////////////////////////////////////////////////////////////////////////////////////////
// This scrolls the viewport to the top of the hashID
// if found, if not it returns to the top of the screen
////////////////////////////////////////////////////////////////////////////////////////////////
function scrollViewPortToDiv( divID, hashID )
{
	// scrollToHash is set when a button with a hash anchor is clicked
	var newYCoord = 0;
	if (hashID.length > 0) newYCoord = getDivYCoord( divID+' '+hashID )-SCROLL_OFFSET;
	scrollViewPortTo( newYCoord );
};
////////////////////////////////////////////////////////////////////////////////////////////////
// This scrolls the viewport to a coord
////////////////////////////////////////////////////////////////////////////////////////////////
function scrollViewPortTo( yCoord )
{
	$jQ('html, body').animate( {scrollTop: yCoord}, SCROLL_DURATION );
};

////////////////////////////////////////////////////////////////////////////////////////////////
// Find the top left Y co-ordinate of a DIV
////////////////////////////////////////////////////////////////////////////////////////////////
function getDivYCoord( divID )
{
	var targetDiv =  $jQ(divID);
	if (!targetDiv) return 0;
	if (targetDiv.length)
	{
		var targetOffset = targetDiv.offset().top;
		if (targetOffset !== undefined) return targetOffset;
		else return 0;
	}
	return 0;
};
/*================== HASH EXTRACTIONS ===================*/

/* Removes everything before the final slash , returning only the filename / location */
function extractLocation( url ){	return ( url.substring( url.lastIndexOf('/') ) || url );	};
/* Removes everything before the final slash : '/' */
function purgeExtension( url ){		return (url.substr(0, url.lastIndexOf('.')) || url);	};
/* Return all data after the first '#' */
function extractHash(hr){	return ( hr.substring( hr.indexOf('#') ) || undefined );	};
/* Remove everything after the hash '#' */
function purgeHash( url ){	return ( url.substring( 0, url.indexOf('#') ) || url );		};
/* Remove the trailing slash at the end of a url */
function deleteTrailingSlash( url ){	return (url.charAt(hr.length-1) == '/') ? url.substring(0, url.lastIndexOf('/')) : url;		};
/* Convert str to Capital First Letter */

/*================== URL Recreation ===================*/

/* Get the BASE_URL eg. http://www.designerzen.com */
function extractBaseLocation () {	return window.location.protocol + '//' + window.location.host;	};

////////////////////////////////////////////////////////////////////////////////////////////////
// get the current URi as a Permalink
// eg. http://www.designerzen.com/category/portfolio/
////////////////////////////////////////////////////////////////////////////////////////////////
function getURi() {		return BASE_URL + convertToPermalink( window.location.pathname );	};

////////////////////////////////////////////////////////////////////////////////////////////////
// append feed/ onto a NON hashed URL.
// eg. http://www.designerzen.com/category/portfolio/feed/
////////////////////////////////////////////////////////////////////////////////////////////////
function getFeedURi() {		return getURi() + '/feed/';	};

////////////////////////////////////////////////////////////////////////////////////////////////
// Recreate a VALID permalink from a relative hashed URi
// This ignores anchored hashes but uses the history
// saved hashes and current AJAX location to form a URi
// eg. designerzen.com/music/#/category/cheese/doodles => designerzen.com/category/cheese/doodles
////////////////////////////////////////////////////////////////////////////////////////////////
function convertToPermalink( uri )
{
	// check to see if it has a #/ (and if so remove after #)
	if ( uri.indexOf('#/') ) uri = uri.substring( uri.indexOf('#/') );
	// strip trailing slash
	if (uri.charAt(hr.length-1) == '/') uri = uri.substring(0, uri.lastIndexOf('/'));
	// TODO: remove AJAX_MODIFIER!!!
	return uri;
};

/*================== Wordpress specific functions ===================*/
// get the post_slug of an article
// www.dz.com/2008/*post_slug*/#/whatever/
function extractPostSlug(hr)
{
	// delete current hashes
	hr = purgeHash( hr );
	// cleanup
	hr = deleteTrailingSlash( hr );

	// extract the post_slug :
	var st = hr.lastIndexOf('/');
	var hash = hr.substring(st+1);
	return hash;
};

function getThemeDir()
{
	if ((THEME_NAME.length > 1)&&(USE_THEME)) return BASE_URL + SUBDIRECTORY + DIRECTORY_TREE + THEME_NAME + '/';
	else return '';
};



/*================== GOOGLE ANALYTIC =================================================================================================================*/

////////////////////////////////////////////////////////////////////////////////////////////////
// GOOGLE ANALYTICS Initialise and Track permalink
////////////////////////////////////////////////////////////////////////////////////////////////
function googleTrackInit()
{
	// If there is a specified ID in the settings, set up the tracker
	if ( GOOGLE_ANALYTICS_ID.length > 5 ) GOOGLE_TRACKER = _gat._getTracker( GOOGLE_ANALYTICS_ID );
	// now if we have google tracker global then track immediately!
	if ( GOOGLE_TRACKER ) GOOGLE_TRACKER._trackPageview();
};

////////////////////////////////////////////////////////////////////////////////////////////////
// GOOGLE ANALYTICS Track virtual link (JS & MP3)
////////////////////////////////////////////////////////////////////////////////////////////////
function googleTrack( breadcrumb )
{
	if ( GOOGLE_TRACKER == null||'')  return;			// FAIL silently
	// check for base url in breadcrumb and strip it back
	if ( breadcrumb.indexOf( BASE_URL)  != -1 ) breadcrumb = breadcrumb.substring( BASE_URL.length );
	if ( breadcrumb.length > 1 )
	{
	    //GOOGLE_TRACKER._initData();
	    if (GOOGLE_TRACKER) GOOGLE_TRACKER._trackPageview( breadcrumb );
	}
};

/*================== Comments / Inputs =================================================================================================================*/
////////////////////////////////////////////////////////////////////////////////////////////////
// This function binds the search function to our internal methods!
////////////////////////////////////////////////////////////////////////////////////////////////
function addSearchFormHandler( divFilter )
{
	// motherfucker! if there are multiple forms on the page, they all have the same id of s!!! wp mfs!
	// check to see if the input button was clicked,
	// or return was pushed when input has focus input[type=submit]
	// #searchform
	if (divFilter == null) divFilter = '';
	$jQ('form.search-form', divFilter).submit(function(evt)
	{
		// check to see if the search form is empty...
		var searchFormSubmit = $jQ( this );
		var searchQuery = $jQ.trim( $jQ( '#s' , searchFormSubmit ).val() );
		if( searchQuery.length < 1 ) {
		  alert("Please search for *something*"); return false;
		}

		searchFormSubmit.ajaxSubmit({
			error:onHtmlFailedToLoad,
			beforeSubmit:onHtmlLoadStart,
			dataFilter:onHtmlReceived,
			success:onSearchResults
		});

		return false;
	})
};

function onSearchResults( response, status )
{
	onHtmlLoaded( response );
	 $jQ( 'input.search-submit-string' ).val( '' );	// empty search fields!
};

////////////////////////////////////////////////////////////////////////////////////////////////
// This function appends the comment box to the current reply to button...
// it is a proxy method for the buyilt in wordpress function of the same named
// Used for threaded comments!
////////////////////////////////////////////////////////////////////////////////////////////////
addComment = {
	moveForm : function(commId, parentId, respondId, postId)
	{
		// find and bind elements
		var tempdiv = "<div id='wp-temp-form-div' style='display:none'></div>",
		responder = $jQ('#'+respondId),
		cancel =  $jQ('#cancel-comment-reply-link',responder),
		post = $jQ("#comment_post_ID"),
		postId = postId || false;

		// exit premature
		if ( ! cancel.length || ! responder.length ) return;

		// create a placeholder for our response form for if you cancel comment or wish for it to return!
		if ( !$jQ('#wp-temp-form-div').length ) $jQ( tempdiv ).appendTo( responder.parent() );

		// status
		ELEMENT_COMMENTS_STATUS.html( " " );
		// now move some stuff around...
		responder.appendTo("#"+commId);

		// set new values (not really sure we need to do this line : )
		if ( post.length && postId ) post.val( postId );	// Set the post ID to the post value
		$jQ("#comment_parent").val( parentId );			// set the parent_ID to the requested comment form

		cancel.show();						// show cancel button :)
		cancel.click( returnReplyForm );			// crete click handler for close link

		$jQ("#comment").focus();				// now focus
		return false;
	}
};
////////////////////////////////////////////////////////////////////////////////////////////////
// This function appends the respond box to it's original location (if it has moved)
////////////////////////////////////////////////////////////////////////////////////////////////
function returnReplyForm( scrollTo )
{
	var tempReplyForm = $jQ('#wp-temp-form-div');
	if (tempReplyForm.length)
	{
		var respond = $jQ('#respond');
		if (respond.length < 1) return false;
		// empty & reset fields!
		$jQ( "#comment_parent", respond ).val( 0 );
		$jQ( "#cancel-comment-reply-link", respond ).hide();		// firstly hide the close button :)
		tempReplyForm.replaceWith( respond );				// over write
		if (scrollTo != false)
		{
			scrollViewPortToElement( respond );
			$jQ("#comment").focus();
		}
	 }
	return false;
};

////////////////////////////////////////////////////////////////////////////////////////////////
// Hijack the current input field and tries to access it in a different way...
// new AJAX load requester & update title and hash
////////////////////////////////////////////////////////////////////////////////////////////////
function addAjaxFormHandlers( commentsDiv )
{
	ELEMENT_COMMENTS_FORM = $jQ( commentsDiv, DIV_DYNAMIC );						// set the constant
	addAjaxFormHandler( ELEMENT_COMMENTS_FORM );
};

////////////////////////////////////////////////////////////////////////////////////////////////
// Hijack a specific element and parse its html
////////////////////////////////////////////////////////////////////////////////////////////////
function addAjaxFormHandler( elements )
{
	ELEMENT_COMMENTS_STATUS = $jQ('#comment-status', elements);
	// firstly find out if there is a #commentStatus element, and if not create one :value='Post Comment'
	if (ELEMENT_COMMENTS_STATUS.length < 1 )
	{
		  // TODO : Add the status @ a specific point in the respond box
		  ELEMENT_COMMENTS_STATUS = $jQ( '<div id="comment-status"></div>' );
		  $jQ('input[id=submit]', elements).after( ELEMENT_COMMENTS_STATUS.html() );	// add loader!
	}
	elements.submit(function(event)
	{
		event.preventDefault();

		var submissionForm = $jQ(this);

		if (!checkCommentFields( submissionForm )) return false;

		// DATA IS GOOD! Submit it...
		submissionForm.ajaxSubmit({

				// Prepare the page...
				beforeSubmit:onCommentSubmitting ,

				// Errors have occured - Display them!
				error: function( request )
				{
					if ( request.responseText.search(/<title>WordPress &rsaquo; Error<\/title>/) != -1 )
					{
						var data = request.responseText.match(/<p>(.*)<\/p>/);
						onCommentFailed( data[1] );
					} else {
						onCommentFailed( request.responseText );
					}

					return false;
				},

				// New Data to inject ... Success!
				success: function( data )
				{

				    var newComments = $jQ('#comments',data);				// ATTEMPT to grab POST data
				    var newHeadline = $jQ('#comments-title',newComments).html();	// ATTEMPT to grab headline's html
				    var oldComments = $jQ('#comments');					// ATTEMPT to grab headlines
				    var oldHeadline = $jQ('#comments-title',oldComments);		// ATTEMPT to grab headlines
				    var commentList = $jQ('ol.commentlist',newComments);		// ATTEMPT to grab comment list
				    returnReplyForm( false );						// Send #RESPOND back down

				    try {
					// update comments
					injectComments( commentList );			// we have a good response from the server - reuse this data model
												// reload this page for now and scroll to the new comment hash

					// update headline
					if ( oldHeadline.length ) oldHeadline.html( newHeadline );
					else oldComments.prepend('<h3 id="comments-title">'+newHeadline+'</h3>');		//else add headline element too?

				    } catch (e) {
					onCommentFailed( 'Error\n : '+e );
				    }

				}

		});
	});
};

////////////////////////////////////////////////////////////////////////////////////////////////
// Validate DATA :
// Check Comment Data for corrections and focus on the incorrect field!
// returns true / false for data is good / bata is bad
////////////////////////////////////////////////////////////////////////////////////////////////
function checkCommentFields( elements )
{
	// Author
	if( elements.find( DIV_COMMENT_AUTHOR )[0] )
	{
		  // Check for Author
		  var author = elements.find( DIV_COMMENT_AUTHOR );
		  if( author.val()  == '' )
		  {
			  ELEMENT_COMMENTS_STATUS.html( createFeedbackMessage(COMMENT_STATUS_NO_NAME,CSS_ERROR) );
			  author.focus();
			  scrollViewPortToElement( author );
			  return false;
		  }
		  // Check for an email address
		  var email = ELEMENT_COMMENTS_FORM.find( DIV_COMMENT_EMAIL );
		  if( email.val() == '' )
		  {
			  ELEMENT_COMMENTS_STATUS.html( createFeedbackMessage(COMMENT_STATUS_NO_EMAIL,CSS_ERROR) );
			  email.focus();
			  scrollViewPortToElement( email );
			  return false;
		  }
		  // Check validity of the email to curb spammers
		  if( !validateEmail( email.val() ) )
		  {
			  ELEMENT_COMMENTS_STATUS.html( createFeedbackMessage(COMMENT_STATUS_BAD_EMAIL,CSS_ERROR) );
			  email.focus();
			  scrollViewPortToElement( email );
			  return false;
		  }
	};

	// Check for an actual comment
	var comment = elements.find( DIV_COMMENT_MESSAGE );
	if( comment.val() == '' )
	{
		ELEMENT_COMMENTS_STATUS.html(createFeedbackMessage(COMMENT_STATUS_NO_COMMENT,CSS_ERROR));
		comment.focus();
		scrollViewPortToElement( comment );
		return false;
	};

	return true;
};

////////////////////////////////////////////////////////////////////////////////////////////////
// first make sure that we actually already have a comment div to populate and if not add one...
function fetchCommentDIV()
{
	var comments = $jQ("ol.commentlist");
	// comment list exists and returned!
	if (comments.length > 0) return comments;
	// create list if it does not exist...
	var newElement =  $jQ( '#respond' ).before('<ol class="commentlist"></ol>');
	return $jQ( "ol.commentlist" );
};

////////////////////////////////////////////////////////////////////////////////////////////////
// User has submitted a comment...
// Show loader!
////////////////////////////////////////////////////////////////////////////////////////////////
function onCommentSubmitting( )
{
	// Remove prior error messages
	$jQ('#respond').addClass( CSS_NOW_LOADING );
	// $jQ('input[@type=submit]', ELEMENT_COMMENTS_FORM)
	$jQ('input[type=submit]').attr("disabled","disabled");	// Disable the submit button And try to Hide it
	ELEMENT_COMMENTS_STATUS.html( createFeedbackMessage(COMMENT_STATUS_SENDING, CSS_NOW_LOADING) );	// Success!
};

////////////////////////////////////////////////////////////////////////////////////////////////
// Comment has been injected into the DOM
////////////////////////////////////////////////////////////////////////////////////////////////
function onCommentAdded(  )
{
	$jQ('#respond').removeClass( CSS_NOW_LOADING );
	ELEMENT_COMMENTS_STATUS.html( createFeedbackMessage(COMMENT_STATUS_SUCCESS,"success") );	// hide the loader
	//$jQ('#respond').hide();									// HIDE the responder
	$jQ( DIV_COMMENT_MESSAGE ).val('');								// EMPTY Form
	$jQ('input[type=submit]').removeAttr("disabled");						// Now enable the submit buttons again
	scrollViewPortToElement( findLatestComment() );							// scroll to the newly added element!
};

////////////////////////////////////////////////////////////////////////////////////////////////
// Comment Data mangled - show error message!
////////////////////////////////////////////////////////////////////////////////////////////////
function onCommentFailed( errorMessage )
{
	$jQ('#respond').removeClass( CSS_NOW_LOADING );
	ELEMENT_COMMENTS_STATUS.html( createFeedbackMessage( "<ins>"+errorMessage+"</ins>",CSS_ERROR ) );
	$jQ('input[type=submit]').removeAttr("disabled").show();
};

////////////////////////////////////////////////////////////////////////////////////////////////
// Inject the DOM with the server response (as html) or reload the list if response empty
////////////////////////////////////////////////////////////////////////////////////////////////
function injectComments( commentElement )
{
	var comments = fetchCommentDIV();
	if ( commentElement.length )
	{
		comments.html( commentElement.html() );
		onCommentAdded();
	}else {
		// If the response from our server is empty due to the way it is setup - do a harder refresh
		comments.load( lastUrl +' '+"ol.commentlist"+' > *' ,AJAX_MODIFIER, onCommentAdded);
	}
};

function createFeedbackMessage( errorMessage, className )
{
	return '<span class="'+className+'">'+ errorMessage +'</span>';
};

////////////////////////////////////////////////////////////////////////////////////////////////
// Find the most recently posted comment element and return it
////////////////////////////////////////////////////////////////////////////////////////////////
function findLatestComment()
{
	var latestComment;
	var previousID = 0
	var numberedID = 0;
	var quantity = 0;
	// loop through the comment list and extract each id...
	$jQ("ol.commentlist li[id^='li-comment-']").each( function(index, domElement)
	{
		  var commentID = this.id;
		  // find the last index of - in the id and extropalte the number of the comment
		  var numberedID = parseInt( commentID.replace("li-comment-",'') );
		  if (numberedID > previousID)
		  {
			  latestComment = $jQ(this);
			  previousID = numberedID;
		  }
		  quantity++;
	});

	if (latestComment) return latestComment;
	else return $jQ( "ol.commentlist" );
};

/* ===== UTILS & STRING PARSING ======= */

function toSentenceCase( str ){	return (str.substr(0,1).toUpperCase()+str.substr(1));		};
/* Convert str to All Capital First Letters */
function toTitleCase( str ){
	var o = '';
	var wrds = str.split(" ");
	for(keyvar in wrds) o += ' ' + toSentenceCase( wrds[keyvar] );
	return o;
};
////////////////////////////////////////////////////////////////////////////////////////////////
// Validate an email address and return true if it is correct!
////////////////////////////////////////////////////////////////////////////////////////////////
function validateEmail( email )
{
	var filter  = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
	if( filter.test( email ) ) return true;
	else return false;
};
function rgb2hex(rgb)
{
      // check to see if already hex!
      if ( rgb.search("rgb") == -1 ) return rgb;
      rgb = rgb.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+))?\)$/);
      function hex(x)
      {
	  return ("0" + parseInt(x).toString(16)).slice(-2);
      }
      return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
};
function isArray(obj) {    return obj.constructor == Array; };

// http://bit.ly/ishiv | WTFPL License
window.innerShiv = function () {
    function h(c, e, b) {
        return /^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i.test(b) ? c : e + "></" + b + ">"
    }
    var c, e = document,
        j, g = "abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video".split(" ");
    return function (d, i) {
        if (!c && (c = e.createElement("div"), c.innerHTML = "<nav></nav>", j = c.childNodes.length !== 1)) {
            for (var b = e.createDocumentFragment(), f = g.length; f--;) b.createElement(g[f]);
            b.appendChild(c)
        }
        d = d.replace(/^\s\s*/, "").replace(/\s\s*$/, "").replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "").replace(/(<([\w:]+)[^>]*?)\/>/g, h);
        c.innerHTML = (b = d.match(/^<(tbody|tr|td|col|colgroup|thead|tfoot)/i)) ? "<table>" + d + "</table>" : d;
        b = b ? c.getElementsByTagName(b[1])[0].parentNode : c;
        if (i === !1) return b.childNodes;
        for (var f = e.createDocumentFragment(), k = b.childNodes.length; k--;) f.appendChild(b.firstChild);
        return f
    }
}();
