/*!
 * jQuery postMessage - v0.5 - 9/11/2009
 * http://benalman.com/projects/jquery-postmessage-plugin/
 *
 * Copyright (c) 2009 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 *
 * Non-jQuery fork by Jeff Lee
 *
 * This fork consists of the following changes:
 * 1. Basic code cleanup and restructuring, for legibility.
 * 2. The `postMessage` and `receiveMessage` functions can be bound arbitrarily,
 *    in terms of both function names and object scope. Scope is specified by
 *    the the "this" context of NoJQueryPostMessageMixin();
 * 3. I've removed the check for Opera 9.64, which used `$.browser`. There were
 *    at least three different GitHub users requesting the removal of this
 *    "Opera sniff" on the original project's Issues page, so I figured this
 *    would be a relatively safe change.
 * 4. `postMessage` no longer uses `$.param` to serialize messages that are not
 *    strings. I actually prefer this structure anyway. `receiveMessage` does
 *    not implement a corresponding deserialization step, and as such it seems
 *    cleaner and more symmetric to leave both data serialization and
 *    deserialization to the client.
 * 5. The use of `$.isFunction` is replaced by a functionally-identical check.
 * 6. The `$:nomunge` YUI option is no longer necessary.
 */

function NoJQueryPostMessageMixin(postBinding, receiveBinding) {

	var setMessageCallback, unsetMessageCallback, currentMsgCallback,
		intervalId, lastHash, cacheBust = 1;

  if (window.postMessage) {

	if (window.addEventListener) {
	  setMessageCallback = function(callback) {
		window.addEventListener('message', callback, false);
	  }

	  unsetMessageCallback = function(callback) {
		window.removeEventListener('message', callback, false);
	  }
	} else {
	  setMessageCallback = function(callback) {
		window.attachEvent('onmessage', callback);
	  }

	  unsetMessageCallback = function(callback) {
		window.detachEvent('onmessage', callback);
	  }
	}

	this[postBinding] = function(message, targetUrl, target) {
	  if (!targetUrl) {
		return;
	  }

	  // The browser supports window.postMessage, so call it with a targetOrigin
	  // set appropriately, based on the targetUrl parameter.
	  target.postMessage( message, targetUrl.replace( /([^:]+:\/\/[^\/]+).*/, '$1' ) );
	}

	// Since the browser supports window.postMessage, the callback will be
	// bound to the actual event associated with window.postMessage.
	this[receiveBinding] = function(callback, sourceOrigin, delay) {
	  // Unbind an existing callback if it exists.
	  if (currentMsgCallback) {
		unsetMessageCallback(currentMsgCallback);
		currentMsgCallback = null;
	  }

	  if (!callback) {
		return false;
	  }

	  // Bind the callback. A reference to the callback is stored for ease of
	  // unbinding.
	  currentMsgCallback = setMessageCallback(function(e) {
		switch(Object.prototype.toString.call(sourceOrigin)) {
		case '[object String]':
		  if (sourceOrigin !== e.origin) {
			return false;
		  }
		  break;
		case '[object Function]':
		  if (sourceOrigin(e.origin)) {
			return false;
		  }
		  break;
		}

		callback(e);
	  });
	};

  } else {

	this[postBinding] = function(message, targetUrl, target) {
	  if (!targetUrl) {
		return;
	  }

	  // The browser does not support window.postMessage, so set the location
	  // of the target to targetUrl#message. A bit ugly, but it works! A cache
	  // bust parameter is added to ensure that repeat messages trigger the
	  // callback.
	  target.location = targetUrl.replace( /#.*$/, '' ) + '#' + (+new Date) + (cacheBust++) + '&' + message;
	}

	// Since the browser sucks, a polling loop will be started, and the
	// callback will be called whenever the location.hash changes.
	this[receiveBinding] = function(callback, sourceOrigin, delay) {
	  if (intervalId) {
		clearInterval(intervalId);
		intervalId = null;
	  }

	  if (callback) {
		delay = typeof sourceOrigin === 'number'
		  ? sourceOrigin
		  : typeof delay === 'number'
			? delay
			: 100;

		intervalId = setInterval(function(){
		  var hash = document.location.hash,
			re = /^#?\d+&/;
		  if ( hash !== lastHash && re.test( hash ) ) {
			lastHash = hash;
			callback({ data: hash.replace( re, '' ) });
		  }
		}, delay );
	  }
	};

  }

  return this;
}