/* comments:
@author: Remy Damour
@date: Sept. 9th, 2008
@copyright: copyleft

global variables list (used for easy configuration): (not set to Piwik.user_options or Piwik.options because this require piwik.js to be loaded first whereas here it can be loaded at any time + left as defined before for backward compatibility)
- piwik_action_name
- piwik_idsite
- piwik_url
- piwik_install_tracker
- piwik_tracker_pause
- piwik_download_extensions

usage:
<script type="text/javascript" src="http://piwik.qc4web.com/piwik.js"></script><noscript><p>Free web analytics <img src="http://piwik.qc4web.com/piwik.php?idsite=1" style="border:0" alt="piwik"/></p></noscript>
<script type="text/javascript">var piwik_script_url = "http://piwik.qc4web.com/piwik.php", piwik_id_site=1;</script>

advantages:
- flexibility
- minimized use of global variables
- yui-compressible (optimized with minimized this.prop calls)
- easier to maintain (documented code)
- <script> can be generated and loaded once dom load event (so that do not delay other scripts due to hostname resolution) (write such example)

Outstanding points:
- Refer to TODO entry in README.txt

tested on FFox, Opera, IE7, IE6; result: OK
*/

try {
	if (Piwik) ;
} catch(e) {
	throw 'Piwik already defined, piwik analysis cancelled.';
}

var Piwik = {
	options: { // each option can be overwritten with corresponding global var: piwik_<option_name>
		install_tracker: 1,
		tracker_pause: 500,
		download_extensions: '7z|aac|avi|csv|doc|exe|flv|gif|gz|jpe?g|js|mp(3|4|e?g)|mov|pdf|phps|png|ppt|rar|sit|tar|torrent|txt|wma|wmv|xls|xml|zip',
		hosts_alias: []
	},
	_plugin_map: {
		dir: ['application/x-director', 'SWCtl.SWCtl.1'],
		fla: ['application/x-shockwave-flash', 'ShockwaveFlash.ShockwaveFlash.1'],
		qt: ['video/quicktime', 'Quicktime.Quicktime'], // Old : "QuickTimeCheckObject.QuickTimeCheck.1"
		rea: ['audio/x-pn-realaudio-plugin', 'rmocx.RealPlayer G2 Control.1'],
		wma: ['application/x-mplayer2', 'wmplayer.ocx'], // Old : "MediaPlayer.MediaPlayer.1"
		pdf: ['application/pdf', ['PDF.PdfCtrl.1', 'PDF.PdfCtrl.5', 'PDF.PdfCtrl.6']]
	},
	_tracker_options: null,
	_ie_plugin_found: null,
	_logged: false,
	
	/**
	 * Start piwik process on DOM ready event
	 *
	 * This function automatically recalls itself until dom is ready. 
	 * It takes an optional argument, that when set to true, consider DOM to be loaded. This argument allow to add <script> node linked to piwik.js from external js file, once domready has been triggered (to allow download delays due to piwik.js stored in external domain). In this case, domready event would already have been triggered and therefore logging function could never get executed. Here, we simply need to call again initialize() with 'true' argument so that content gets logged (and if already done, no pbm since we have a variable checking this to avoid logging doublons: this._logged)
	 * @param bool dom_is_ready Optional // can be removed, user simply needs to turn Piwik._dom_loaded = true;
	 */
	initialize: function(/*dom_is_ready*/)
	{
		var action_name = this._getOption('action_name', document.title),
			id_site = this._getOption('idsite', 1),
			php_script_url = this._getOption('url', window.location.hostname+'/piwik/piwik.php');
		this.log(action_name, id_site, php_script_url);
	},
	
	/**
	 * Proceed to piwik information logging
	 *
	 * Call php function throw img uri. Wrap img node within <p> node to be XHTML DTD compliant + hide p node (does not use display:none; since this could get the image not to be generated, ie. data not to be logged)
	 * @param string action_name
	 * @param int id_site
	 * @param string piwik_url
	 * @param array custom_vars
	 */
	log: function(action_name, id_site, piwik_url, custom_vars)
	{
		//if (this._logged && (!action_name || action_name == '')) return;
		if (this._logged) return;
		var div = document.createElement('div'), log_uri = this._getUrlLog(action_name, id_site, piwik_url, custom_vars);
		div.innerHTML = '<p style="visibility:hidden;width:0;height:0;display:inline;"><img src="'+log_uri+'" alt="Piwik" /></p>';
		document.body.appendChild(div.firstChild);
		//if (!action_name || action_name == '') this._logged = true;
		this._logged = true;
		this._initTracker(id_site, piwik_url);
	},
	
	/**
	 * Called to track followed links
	 * 
	 * @param string link_url
	 * @param string link_type ('download'|'link') only 'download' and 'link' types supported
	 */
	track: function(link_url, link_type)
	{
		//alert("link_url : "+link_url);
		//alert("link_type : "+link_type);
		//alert('this._tracker_options.url : '+this._tracker_options.url);
		//alert('this._tracker_options.id_site : '+this._tracker_options.id_site);
		var image = new Image(), piwik_url = this._tracker_options.url, id_site = this._tracker_options.id_site;
		image.onLoad = function() {};
		image.src = 'http://www.bigyouth.fr/stats/piwik.php?idsite=' + id_site + '&' + link_type + '=' + escape(link_url) + '&rand=' + Math.random() + '&redirect=0';
		//alert(image.src);
		this._pause( this._tracker_options.tracker_pause );
	},
	
	/**
	 * Take a string as input and returned escaped version
	 *
	 * @param string txt
	 * @return string
	 */
	escape: function(txt)
	{
		if(typeof(encodeURIComponent) == 'function') {
			return encodeURIComponent(txt);
		} else {
			return escape(txt);
		}
	},
	
	/**
	 * Browser agent detection, return true if windows+IE environment, false otherwise
	 *
	 * @return bool
	 */
	_isWindowsTest: function()
	{
		var agent = navigator.userAgent.toLowerCase(), ie = agent.indexOf("msie") != -1, win = (agent.indexOf("win") != -1) || (agent.indexOf("32bit") != -1);
		return ie && win;
	},
	
	/**
	 * Cookie status detection
	 *
	 * @return bool
	 */
	_isCookieEnabled: function()
	{
		var cookie_enabled = navigator.cookieEnabled;
		if(typeof (cookie_enabled) == "undefined") {
			document.cookie="_pk_testcookie"
			cookie_enabled = (document.cookie.indexOf("_pk_testcookie") != -1);
		}
		return !!cookie_enabled;
	},
	
	/**
	 * Detect installed plugins
	 *
	 * @param bool windows_test
	 * @return {} Object with keys of this._plugin_map and values among ('0'|'1')
	 */
	_getPlugins: function(windows_test)
	{
		var plugin_list = {}, mime_types = this._getMimeTypes(windows_test);
		for (plugin_type in this._plugin_map) {
			plugin_list[plugin_type] = this._testPlugin(plugin_type, mime_types, windows_test);
		}
		return plugin_list;
	},
	
	/**
	 * Concatenate all existing mime types on non IE + WINDOWS config
	 *
	 * @param bool windows_test
	 * @return string
	 */
	_getMimeTypes: function(windows_test)
	{
		var mime_types = '';
		if (!windows_test) {
			for (var i=0; i < navigator.mimeTypes.length; i++) {
				mime_types += navigator.mimeTypes[i].type.toLowerCase();
			}
		}
		return mime_types;
	},
	
	/**
	 * Test presence of passed plugin id
	 *
	 * @param string plugin_type
	 * @param string mime_types
	 * @param bool windows_tester
	 * @return string ('0'|'1') '0' == plugin not found, '1' == plugin found
	 */
	_testPlugin: function(plugin_type, mime_types, windows_tester)
	{
		var tester = this._getPluginTester(windows_tester), plugin_names = this._plugin_map[plugin_type][windows_tester ? 1 : 0];
		if (typeof(plugin_names) == 'string') plugin_names = [plugin_names];
		for (var i=0; i < plugin_names.length; i++) {
			if (tester(plugin_names[i], mime_types)) return '1';
		}
		return '0';
	},
	
	/**
	 * Return appropriate plugin tester based on client's platform
	 *
	 * IE testing is not implemented yet (we generate a vbscript node => must wait for content to be generated => should use a Piwik.IEPluginTested function call?)
	 * @return function
	 */
	_getPluginTester: function(windows_tester)
	{
		if (windows_tester) {
			return function(plugin_name) {
				Piwik._ie_plugin_found = false;
				var script = document.createElement('script');
				script.type = 'text/vbscript';
				script.text = '\n on error resume next \n Piwik._ie_plugin_found = IsObject(CreateObject("' + plugin_name + '")) ';
				document.body.appendChild(script);
				if (script.parentNode) document.body.removeChild(script);
				return Piwik._ie_plugin_found ? '1' : '0';
			};
		} else {
			return function(plugin_name, mime_types) {
				return (mime_types.indexOf(plugin_name) != -1 && (navigator.mimeTypes[plugin_name].enabledPlugin != null));
			};
		}
	},
	
	/**
	 * Return referrer, if any or empty string
	 *
	 * @return string
	 */
	_getReferrer: function()
	{
		var referrer = null;
		try {
			referrer = top.document.referrer;
		} catch(e1) {
			if(parent){ 
				try{ referrer = parent.document.referrer; } catch(e2) { referrer = null; }
			}
		}
		if(!referrer) {
			referrer = document.referrer;
		}
		return referrer;
	},
	
	/**
	 * Return piwik url to be called
	 *
	 * @param string action_name
	 * @param string id_site
	 * @param string piwik_url
	 * @param array|null custom_vars
	 * @return string
	 */
	_getUrlLog: function(action_name, id_site, piwik_url, custom_vars)
	{
		var custom_vars_str = '';
		if (custom_vars) {
			for (var i in custom_vars){
				if (!Array.prototype[i]){
					custom_vars_str = custom_vars_str + '&vars['+ escape(i) + ']' + "=" + escape(custom_vars[i]);
				}
			}
		}

		var cookie = this._isCookieEnabled() ? '1' : 0, 
			plugin_list = this._getPlugins( this._isWindowsTest() ), 
			referrer = this._getReferrer(),
			java = navigator.javaEnabled() ? '1' : '0',
			title = this.escape(document.title),
			current_date = new Date();

		return piwik_url
			+'?url='+this.escape(document.location.href)
			+'&action_name='+this.escape(action_name)
			+'&idsite='+id_site
			+'&res='+screen.width+'x'+screen.height+'&col='+screen.colorDepth
			+'&h='+current_date.getHours()+'&m='+current_date.getMinutes()+'&s='+current_date.getSeconds()
			+'&fla='+plugin_list['fla']+'&dir='+plugin_list['dir']+'&qt='+plugin_list['qt']+'&realp='+plugin_list['rea']+'&pdf='+plugin_list['pdf']
			+'&wma='+plugin_list['wma']+'&java='+java+'&cookie='+cookie
			+'&title='+title
			+'&urlref='+this.escape(referrer)
			+custom_vars_str;
	},
	
	/**
	 * Init tracker
	 *
	 * @param int id_site
	 * @param string piwik_url
	 */
	_initTracker: function(id_site, piwik_url)
	{
		var install_tracker = this._getOption('install_tracker'), 
			tracker_pause = this._getOption('tracker_pause'), 
			download_extensions = this._getOption('download_extensions'),
			hosts_alias = this._getOption('hosts_alias');
		
		if(!install_tracker) return;
		
		hosts_alias.push(window.location.hostname);
		this._tracker_options = {
			tracker_pause: tracker_pause,
			hosts_alias: hosts_alias,
			id_site: id_site,
			url: piwik_url
		};
		
		if (document.getElementsByTagName) {
			links_elements = document.getElementsByTagName('a');
			for (var i = 0; i < links_elements.length; i++) {
				if( links_elements[i].className.indexOf('piwik_ignore') == -1 )
					this._addEvent(links_elements[i], 'mousedown', this._onClickEvent, false);
			}
		}
	},
	
	/**
	 * Add event listener, browser independent
	 *
	 * @param Element el
	 * @param string event_type
	 * @param function fn
	 * @param bool use_capture
	 */
	_addEvent: function(el, event_type, fn, use_capture)
	{
		var bind = this, binded_fn = function() {return fn.apply(bind, arguments);};
		if (el.addEventListener) { 
			el.addEventListener(event_type, binded_fn, use_capture); 
		} else if (el.attachEvent) { 
			el.attachEvent('on' + event_type, binded_fn);
		} else {
			el['on' + event_type] = binded_fn;
		}
	},
	
	/**
	 * Each option can be overwritten with corresponding global var prefixed with "piwik_"
	 *
	 * For instance, option "install_tacker" can be overwritten with global var "piwik_install_tracker"
	 * @param string option_name
	 * @param mixed default_value Optional
	 * @return mixed
	 */
	_getOption: function(option_name, default_value)
	{
		try {
			return eval('piwik_'+option_name);
		} catch (e) {
			return default_value ? default_value : this.options[option_name];
		}
	},
	
	/**
	 * Called on click event, to track clicked urls
	 *
	 * Always return true not to stop event propagation and default behaviour of click event
	 * @param Event e
	 * @return true
	 */
	_onClickEvent: function(e)
	{
		var source;
		if (typeof e == 'undefined') var e = window.event;
		if (typeof e.target != 'undefined') source = e.target;
		else if (typeof e.srcElement != 'undefined') source = e.srcElement;
		else return true;

		while( source.tagName.toUpperCase() != 'A' )
			source = source.parentNode;
		if( typeof source.href == 'undefined' ) return true;

		var preg_download = new RegExp('\\.(' + this._getOption('download_extensions') + ')$', 'i'), 
			not_site_hostname = !this._isSiteHostname(source.hostname),
			link_type;
		if( source.className.indexOf('piwik_download') != -1 ) {
			link_type = 'download';
		} else if( source.className.indexOf('piwik_link') != -1 ) {
			link_type = 'link';
			not_site_hostname = 1;
		} else link_type = preg_download.test(source.href) ? 'download' : 'link';

		if( not_site_hostname || link_type == 'download' ) 
			this.track(source.href, link_type);
		
		return true;
	},
	
	/**
	 * Check if passed name is among defined hostname alias
	 *
	 * @param string hostname
	 * @return bool
	 */
	_isSiteHostname: function(hostname)
	{
		for(var i = 0; i < this._tracker_options.hosts_alias.length; i++)
			if( hostname == this._tracker_options.hosts_alias[i] ) 
				return true;
		return false;
	},
	
	/**
	 * Function used to generate a pause
	 *
	 * Do not use setTimeout() (does not work to prevent link from being followed) or preventDefault on click event (would interfeer too much with user's js event listeners if any)
	 * @param int time_msec
	 */
	_pause: function(time_msec)
	{
		var now = new Date();
		var expire = now.getTime() + time_msec;
		while(now.getTime() < expire)
			now = new Date();
	}

};

Piwik.initialize();
