/*****************************************************************************
RightContext v0.2.5
Author: Harel Malka  
http://www.harelmalka.com 
harel@harelmalka.com
Bugs: Leave comment on the site

February 2007 - Initial Revision
----------------------------------------------------------------------------

RightContext is a supercharged context menu. I created it to 
answer a few requirements I had of context menus which I could not find 
anywhere else; mainly provide the *correct* contextualized links depending 
on what was triggering the menu. Originaly this was a Right Click menu only, 
though this was changed in 0.2.4 to allow menu triggering by Left/Right Click,
or Mouse over actions. 
RightContext generates unique context menus that are built based on the DOM 
element clicked using special attributes embedded in the element's html. 
The attributes are custom generated and can be anything. Menu items can 
include [tags] referencing those attributes, which will cause them to be 
transformed to contain the actual values when the menu is constructed 
(i.e., when an element is left/right clicked or moused over, depending on 
the menu's setting.)

Some of the key features of RightClick:
	* Trigger items via Right click, Left click or Mouse over (new in 0.2.4) 
	* Menu items that link somewhere
	* Menu items that perform a custom javascript function
	* Menu items that display hardcoded text
	* Menu items that retrieve text via a remote 'ajax' call
	* Menu item separators
	* Supports multiple different menus that can be called depending on 
		the element clicked
	* All menu items can contain [tags] which are transformed at runtime to 
		the values embedded in the clicked element
	* Conditional evaluation of menu items. An item can show or not show depending
		on a specified condition in the menu template (new in v0.2.3)
	* Css based look and feel
	* Unobtrusive standalone javascript: no additional js framework required.

RightContext is the menu object. It contains a collection of context menu 
definitions which can be retrieved by unique names. A context menu will 
receive a set of arguments from any calling Context that can be used to 
construct the links or text in it. 
This is a singleton object that should have one instance per page.

A few of the object's properties that you can edit:
	menuTriggerEvent -> Can be either RIGHT (default), LEFT, or MOVE to 
						determine what triggers the menus generated (left/right
						click or a mouse over event). Value is in UPPER case.
	allowedContexts  -> A String array of tag names that can have a menu attached
						to them. The default list covers A, DIV, SPAN, INPUT tags.

A menu definition is an object with the following spec:

	menu1 = { attributes: "attr1,attr2,attr3" ,

				items: [ 
					{type:RightContext.TYPE_MENU,
						text:"Search for [attr2] on Google",
						url:"http://www.goole.com?q=[attr1]",
						image: "icon.gif", align:"right" },

					{type:RightContext.TYPE_MENU,
						text:"The second item in the menu. it will show if attr3=Y (and its: [attr3])",
						requires: ["attr3", "Y"],
						onclick:function() { alert('This is a custom javascript')} },
					
					{type: RightContext.TYPE_SEPERATOR },
					
					{type: RightContext.TYPE_TEXT,
						text: "This is hardcoded, yet dynamic text: attr1=[attr1], attr2=[attr2], attr3=[attr3]"} ,
					
					{type: RightContext.TYPE_TEXT_EXT,
						url: "external.html"}   ]
			};

Each menu definition object contains two properties: 

	attributes : a comma delimited list of attributes that this menu will 
				look for in the elements its bound to
	items      : an array of objects, each describes a menu item of a given 
				type. The example above covers all 
				currently supported types and their respective properties.   

A couple of notes about item properties:
	* Use either url or onclick for TYPE_MENU items - url will take precedence 
	over onclick. 
	* For url type menu items, you can add an optional frame property to redirect
	the link to a specific frame, or use _blank to open a new window
	* The image attribute for TYPE_MENU are optional.    
	* For TYPE_MENU, you can use the optional 'requires' property. It accepts 
	an array with two elements; the first one is an attribute to evaluate and 
	the second is what it should evaluate to if this menu item is to be displayed.

After creating some menu definitions, you need to add them to the menu 
collection within RightContext by doing:

	RightContext.addMenu("menuOne", menu1); 
	
This adds menu1 and tags it with unique name: menuOne. The unique name is how 
we'll refer to this menu later.

Not all html tags are used to allow context menus. By default, only 
A, DIV, SPAN and INPUT tags are considered 'contextualizable' but you can add 
additional tags by editing the allowedContexts property of the RightContext 
object. 

To generate a context menu for a particular html tag, first add the context 
attribute to it, and then any additional attributes that you'd like it to 
have. The context attribute holds which menu to call up.
The additional ones are the attributes defined in the attributes property of 
the menu definition. 
For the menu definition above, here's an A tag example:

	<a href="http://www.freecrm.com" context="menuOne" attr1="freecrm" 
						attr2="FreeCRM.com" attr3="Hi mum!">Right Click Me</a>
	
	The above link will use the menuOne (mapped to menu1) defintion. When 
	triggered, the first menu item will a link titled 
	"Search for freecrm on Google" ([attr1] is replaced by the tag's 
	attribute value - freecrm)
	The second item will pop a javascript alert and will be titled 
	"The second item in the menu. attr3=Hi Mum!".
	It will be followed by a separator and then the following text:
	"This is hardcoded, yet dynamic text: attr1=freecrm, attr2=FreeCRM.com,
	attr3=Hi mum!"
	The last item will retrieve the contents of the external.html file 
	provided. Obviously, you can also embed url parameters in the url as tags.
	Note that the link above will cause the second menu item to not display
	because the attr3 attribute is equal to "Hi mum!" while the menu template
	defines that the second item will only show if the attr3 is equal to Y.

You can create as many menu definitions as needed for different elements on 
your page. Simple define which menu to call in the tag's context attribute.

Finally, to kick of the party and start some context menu action going, you 
need to bind the tags that contain the context attribute to their respectful 
menus by doing: 

	RightContext.initialize();

Please see the example file provided (index.html). An example is worth 
a thousand comments...

CSS Notes:
The RightContext object makes use of some CSS classes defined in the
rightcontext.css file. You need to include that in your html, or make use
of these classes yourself.

Some credits: 
I was always on the lookout for the right context menu but was too busy (or 
lazy) to make my own. What finally broke the camel's back was a ContextMenu 
script using prototype that i found here:
http://www.ajaxline.com/node/338 (note - last i checked the link was not 
active). But, it wasn't stand alone, and it wasn't really "contextualize" 
to what's clicked. I wasn't a happy puppy, So i rolled up my sleeves. 
I used the getDimensions method from prototype since it worked great and 
saved me time doing my own. (http://prototype.conio.net)
I also adapted the logic in Alessandro Fulciniti great bubble tooltip 
script's Locate method for the locateMousePos (slightly modified)
method for the same reason (http://pro.html.it  and  
http://web-graphics.com )
Also thanks to all the good people who bothered to comment about this script.
Thanks to them I added the left click and mouse over trigger options.
In 0.2.5 I also incorporated fixes provided by Ian, CraigD, David Zhang and fanno. 
So thanks guys!

LICENSE:
This script is FREE to use and modify for personal or commercial purposes. 
I only ask that you either leave this commented message intact, OR leave 
credit where credit is due, with all URLs provided (not just to myself, 
but also to prototype and Alessandro).
As well, if you make any modifications to this script that enhances its 
functionality, please DO give them back to the world and please email them 
to me so that I can consider including them into my own version here. 
I'd also love to hear of any good use you've made with this script!

WARRANTY: 
This code comes with NO Warranty whatsoever. I cannot be help responsible
for any damage of any kind to any thing resulting from using this code.
Do NOT use this code for NASA shuttle launches! It won't work. 

Please send bug reports to rightcontext@googlemail.com

Have fun

CHANGE LOG -----------------------------------------------------------------

0.2   : 12 Feb 2007  Added support for tags in menu onclick event handlers.
					Added a couple of fixes provided by JDG including a Safari
					fix.
0.2.1 : 17 Feb 2007  Bug fix: event handler parsing was overwriting the template
					with the first parsed item.
0.2.2 : 20 Feb 2007  Bug fix: IE6-7 issue with onlclick events containing tags 
					and a menu positioning fix.
0.2.3 : 20 Feb 2007  Added conditional display of menu items based on [tag] values
0.2.4 : 26 Feb 2007  Added menu triggering via LEFT click and mouse over (MOVE)
					events.
0.2.5 : 04 Oct 2008  Fixes tags not passing properly on FF3 with onclick events.
					Also incorporates all fixes proposed by users. 
					Thanks to Ian, CraigD, David Zhang and fanno
					
****************************************************************************/

var RightContext = {
	//some final vars:
	TYPE_MENU: 0,       // menu item
	TYPE_TEXT: 1,       // inline text (non-mutable hard coded)
	TYPE_TEXT_EXT: 2,   // external text (retrived via rpc call)
	TYPE_SEPERATOR:3,   // separator line
	TYPE_ATTRIBUTES:4,  // menu attributes.

	// some simple browser detection
	browser: null,

	// set the event to trigger the menus: RIGHT,LEFT (right/left click) or mouse MOVE 
	menuTriggerEvent: "RIGHT",
	
	// object to hold temp mouse position
	mousePos: {x:0, y:0},

	// offset for menu from mouse pointer
	rightOffset: 5,
	
	// kill menu timeout - sets the timeout from mouse out to menu dissapearing
	killMenuTimeout: 25, 

	// type of html tags that can have context menus. You can edit this to 
	// allow more tags into the party.
	allowedContexts: ["a","div","span","input"],

	// object to hold a collection of menus indexed by name
	menuCollection: new Object(),

	// the currently visible context menu DIV element
	contextMenu: null,
	
	// some state machine: is the menu showing (LEFT), and should killing it be aborted (MOVE)
	isShowing: false,
	abortKill: false,
	
	// image cache
	images: new Object(),
	
	// var to hold external requests
	req: null,
	
	// initialize RightContext object 
	initialize: function () {
		this.browser = RightContext.detectBrowser();
		this.attachContextEvents();   
	},

	// adds a menu to the menuCollection
	addMenu: function (n, m) { 
		this.menuCollection[n] = m;
	},

	// return a menu from the menu collection
	getMenu: function (n) {
		return this.menuCollection[n];
	},

	// loop all context allowed tags in the document and attach menu events to 
	// those that contain the menu attribute
	attachContextEvents: function () {
		var tagContext, thisTag;
		for (var t=0; t<this.allowedContexts.length; t++) {
			tags = document.getElementsByTagName(this.allowedContexts[t]);

			for (e=0; e<tags.length; e++) {
				thisTag = tags[e];
				tagContext = thisTag.getAttribute("context");
				if (tagContext!=null && tagContext != "undefined") {
					this.bindEvent('mousemove', tags[e], function(e) { return RightContext.locateMousePos(e); });
					if (this.menuTriggerEvent=="RIGHT") {
						tags[e].oncontextmenu = function() {   return RightContext.render(this);   }; 
					} else if (this.menuTriggerEvent=="LEFT") {
						//this.bindEvent('click', tags[e],  function() {  return RightContext.render(this, tagContext);  });
						tags[e].onclick = function(e) { 
														RightContext.killBubble(e); 
														return RightContext.render(this)
													};
						tags[e].onmouseout = function(e) { setTimeout("RightContext.killMenu()", 5000);};
					} else if (this.menuTriggerEvent=="MOVE") {
						if (!document.all) {
							this.bindEvent('mouseover', tags[e], function(e) { RightContext.locateMousePos(e); return RightContext.render(this); });
							this.bindEvent('mouseout',  tags[e], function(e) { setTimeout("RightContext.killMenu()", RightContext.killMenuTimeout); });
						} else {
							tags[e].onmouseover =  function(e) { RightContext.locateMousePos(e); return RightContext.render(this); };
							tags[e].onmouseout = function(e) { setTimeout("RightContext.killMenu()", RightContext.killMenuTimeout); };
						}
					}
				}
			}
		}
	},
	
	killBubble: function(e) {
		if (!e) var e = window.event;
		e.cancelBubble = true;
		if (e.stopPropagation) e.stopPropagation();
	},
	
	// binds an event handler to an object
	bindEvent: function (evt, obj, act, bubble) {
		if (!bubble) bubble = false;
		if (obj.addEventListener) {
			obj.addEventListener(evt, act, bubble);
		} else if (obj.attachEvent) {
			obj.attachEvent('on'+evt, act);
		}
	},
	

	/*
	renders a given menu and attaches it to the caller object.
	The caller is responsible to contain a few extra attributes
	that will help construct the links for this menu (i.e., provide the Context)
	*/
	render: function (caller, name) {
		var url, title;
		// if name was not specified, grab it from the caller
		// v0.2 - changed to getAttribute (used direct nodeValue access before my mistake). pointed out by JDG.
		var name = name || caller.getAttribute("context");

		// get the requested menu
		var thisMenu = this.getMenu(name);
		
		// extracts this menus attributes list and items
		var attributes = thisMenu["attributes"].split(',');   
		var items = thisMenu.items;

		// constructs a map from the callers attributes
		var objMap = this.buildAttributeMap(attributes, caller);

		// start building the menu itself, but first remove menu if visible
		this.killMenu();
		this.buildMenu(caller);
		
		// create a table to build the menu items in
		tbl = document.createElement("TABLE");
		tbl.id = "rcRightContextTable";
		
		// loop the menu items and render each according to its type
		for (var m=0; m<items.length; m++) {
			switch (items[m]["type"]) {
				case this.TYPE_MENU:
					// add the menu item
					if (this.isDisplayed(items[m], objMap)) {
						this.addMenuItem(items[m], objMap, tbl);
					}
					break;

				case this.TYPE_TEXT:
					// add fixed text
					text = this.transform(items[m]["text"], objMap);
					cell = this.addTableCell(tbl, "rcMenuItemText", text);
					break;

				case this.TYPE_TEXT_EXT:
					cell = this.addTableCell(tbl, "rcMenuItemTextExt");
					url = this.transform(items[m]["url"], objMap);
					this.request(url, function() { if (RightContext.req.readyState == 4 && RightContext.req.status == 200) { cell.innerHTML = RightContext.req.responseText } });
					break;

				case this.TYPE_SEPERATOR:
					cell = this.addTableCell(tbl);
					cell.appendChild(this.getSeparator());
					break;
				
				default:
					// no default behaviour
					break;
			}
			
		}
		// append the menu item table to the menu itself
		this.contextMenu.appendChild(tbl);
		// make sure we're not overflowed to the edge of the screen.
		this.repositionMenu();
		
		if (this.menuTriggerEvent=="MOVE") {
			this.bindEvent('mouseout',  this.contextMenu, function(e) { RightContext.abortKill = false; setTimeout("RightContext.killMenu()", RightContext.killMenuTimeout); });
			this.bindEvent('mouseover', this.contextMenu, function(e) { RightContext.abortKill = true;  });
		} else if (this.menuTriggerEvent=="LEFT" || this.menuTriggerEvent=="RIGHT") {
			this.bindEvent('click', document.body, function(e) { setTimeout("RightContext.killMenu();", RightContext.killMenuTimeout); }, false);
		} 
		this.isShowing = true;
		
		return false;
	},

	isDisplayed : function(item, objMap) {
		var reqVar, reqVal;
		var shown = true; // by default all items are shown, unless they require something 
		// lets make sure this item does not require any condition to be true in order to display
		if (item["requires"] != null && item["requires"] != "undefined") {
			// yep, this one has a requirement...
			reqVar = item["requires"][0];
			reqVal = item["requires"][1];
			if (objMap[reqVar] != null && objMap[reqVar] != "undefined") {
				// if the condition is not met, do not show this item.
				if (objMap[reqVar] != reqVal) {
					shown = false;    
				}
			} else {
				// if the condition is not defined do not show the item
				shown = false;
			}
		}
		return shown;
	},
	
	// check if the menu goes outside the window boundries and adjust its 
	// location if so
	repositionMenu: function() {
		/*var mPos = this.findPosition(this.contextMenu);
		var mDim = this.getDimensions(this.contextMenu);
		var winHeight = this.getWindowHeight(); // window.innerHeight || document.body.clientHeight;   
		var winWidth = window.innerWidth || document.body.clientWidth;
		if (mPos.y + mDim.height > winHeight-30 ) {
			this.position(this.contextMenu, mPos.x, mPos.y - mDim.height);
			mPos = this.findPosition(this.contextMenu);
		} 
		if (mPos.x + mDim.width > winWidth - 30 ) {
			this.position(this.contextMenu, mPos.x-mDim.width, mPos.y);
		} */
	},
	
	// returns an HR sepearator which uses the rcMenuSeparator style
	getSeparator: function () {
		var sep = document.createElement("HR");
		sep.className = "rcMenuSeparator";
		return sep;
	},
	
	// adds a table cell to the provided table and returns it.
	// attached a class if provided and initializes the cell with some content 
	// where applicable
	addTableCell: function (table, className, content) {
		row = table.insertRow(-1);
		cell = row.insertCell(0);
		if (className) { 
			cell.className = className;
			if (content) {
				cell.innerHTML = content;
			}
		}
		return cell;
	},

	// adds a menu item to the provided table. transforms all data as defined 
	// in the objMap argument
	addMenuItem: function (item, objMap, tbl) {
		var title = this.transform(item["text"], objMap);
		var url, frame, img, imgAlign, itemSrc, tmp, itemAction; 
		var cell = this.addTableCell(tbl, "rcMenuItem", title); 
		cell.style.cursor = document.all?'hand':'pointer';
		this.bindEvent('mouseover', cell, function(e) { cell.className="rcMenuItemHover";});
		this.bindEvent('mouseout',  cell, function(e) { cell.className="rcMenuItem";     });
		
		// deal with image if applicable
		if (item["image"]!=null && item["image"]!="undefined") {
			// get image alignment or default to absmiddle
			imgAlign = (item["align"]!=null && item["align"]!="undefined") ? item["align"] : "absmiddle";
			// load the image from the cache, or from disk (and then cache it)
			if (this.images[item["image"]] != null && this.images[item["image"]] != "undefined") {
				img = this.images[item["image"]];
			} else {
				img = this.loadImage(item["image"]);
			}
			// set image alignment
			img.align=imgAlign;
			// insert the image as first child of the cell
			cell.insertBefore(this.images[item["image"]], cell.childNodes[0]);
		}
		
		if (item["url"]!=null && item["url"] != "undefined") {
			url   = this.transform(item["url"],  objMap);
			frame = false;
			if (item["frame"] != null && item["frame"] != "undefined") {
				frame = item["frame"];
			}
			cell.onclick = function () { RightContext.redirect(url, frame); }
		} else {
			// we first need to find out if the event handler contains a potential 
			// tag. if so, we grab its source, transform it and re-evaluate it. 
			// if this fails, the value reverts back to its original function
			itemAction = item["onclick"]; 
			try {

				itemSrc = item["onclick"].toString();
				if (itemSrc.indexOf('[')>-1) {
						itemSrc = this.transform(itemSrc, objMap);
						eval('itemAction = ' + itemSrc);
				}
			
			} catch (e) {
				// nothing...
			}

			// set the cell onclick event handler.
			cell.onclick=itemAction;
		}

	},

	// transforms a string based on the provided map
	transform: function (str, map) {
		var tStr, tmp;
		tStr = str;
		for (p in map) {
			tmp = "[" + p + "]";
			while (tStr.indexOf(tmp) > -1) {
				tStr = tStr.replace(tmp, map[p]);
			}
		}
		return tStr;
	},

	// returns the menu's attributes collection that will be used to construct 
	// the transformation map
	getMenuAttributeArray: function (menu) {
		for (var i=0; i<menu.length; i++) {
			if (menu[i].type == this.TYPE_ATTRIBUTES) {
				return menu[i]["attributes"].split(',');
			}
		}
		return new Array(0);
	},

	// construct the transformation map for a given object based on the tags in 
	// attribs
	buildAttributeMap: function (attribs, obj) {
		var thisAttr, thisValue;
		var attrMap = new Object();

		for (var a=0; a<attribs.length; a++) {
			thisAttr = attribs[a];
			thisValue = obj.getAttribute(attribs[a]);
			if (typeof thisValue != "undefined") {
				attrMap[thisAttr] = thisValue;
			}
		}
		return attrMap;
	},

	// find the position of an element on the screen and returns an array of [x,y]
	findPosition: function (obj) {
		var lft = 0;
		var top = 0;
		if (obj.offsetParent) {
			lft = obj.offsetLeft
			top = obj.offsetTop
			while (obj = obj.offsetParent) {
				lft += obj.offsetLeft
				top += obj.offsetTop
			}
		}
		return {x:lft,y:top};
	},
	
	getWindowHeight: function() {
		if (this.browser.khtml || this.browser.safari) {
			return this.innerHeight;
		} else if (this.browser.opera) {
			return document.body.clientHeight;			
		} else {
			return document.documentElement.clientHeight;
		}
	},

	// Returns the dimensions of an element on screen. Lifted from the wonderful 
	// prototype framework
	getDimensions: function(obj) {
		//var display = obj.getStyle('display');
		//if (display != 'none' && display != null) // Safari bug
		//  return {width: element.offsetWidth, height: element.offsetHeight};

		// All *Width and *Height properties give 0 on elements with display none,
		// so enable the element temporarily
		var objStyle = obj.style;
		var originalVisibility = objStyle.visibility;
		var originalPosition = objStyle.position;
		var originalDisplay = objStyle.display;
		objStyle.visibility = 'hidden';
		objStyle.position = 'absolute';
		objStyle.display = 'block';
		var originalWidth = obj.clientWidth;
		var originalHeight = obj.clientHeight;
		objStyle.display = originalDisplay;
		objStyle.position = originalPosition;
		objStyle.visibility = originalVisibility;
		return {width: originalWidth, height: originalHeight};
	},

	// positions object at x,y coordinates
	// v0.2 - added px to the position coordinate (provided by JDG)
	position: function (obj, x, y) {
		obj.style.left = x + 'px';
		obj.style.top  = y + 'px';
	},

	// builds a menu for parent object
	buildMenu: function (parent) {
		var pos, dim, tbl, widthOffset;
		//document.onmousemove  = RightContext.getMousePos;
		this.contextMenu = document.createElement("DIV");
		this.contextMenu.id = "rcRightContext";
		this.contextMenu.className = 'rcMenuContainer';

		// get the position and dimensions of the parent
		//pos = this.findPosition(parent);
		//dim = this.getDimensions(parent);
		widthOffset = -5;
		if (this.getWindowWidth() > 1000) {
			widthOffset = Math.round((this.getWindowWidth() - 1000) / 2)  - 10;
		}

		// position the container to the bottom right of the element.
		//this.position (this.contextMenu, this.mousePos.x + this.rightOffset, pos.y+dim.height);
		this.position (this.contextMenu, this.mousePos.x - widthOffset, this.mousePos.y);

		// set some event handlers
		// if the menu is triggered by a right click, disable the right click on the menu itself
		if (this.menuTriggerEvent == "RIGHT") {
			this.contextMenu.oncontextmenu = function () { return false; };
		}
		
		// add the container to the body of the document
		document.body.appendChild(this.contextMenu);
		
	},


	// kills the currently visible context menu
	killMenu: function () {
		if (!this.abortKill && this.isShowing) {
		try {
			rc = this.contextMenu;
			document.body.removeChild(rc);
		} catch (e) {
			// already removed?
		}
		this.contextMenu = null;
		this.isShowing = false;
		this.abortKill = false;
		}
	},

	// locate the mouse cursor position 
	locateMousePos: function(e) {
		var posx = 0, posy =0;
		if(e==null) e=window.event;
		if(e.pageX || e.pageY) {
			posx=e.pageX; posy=e.pageY;
		} else if (e.clientX || e.clientY) {
			if(document.documentElement.scrollTop){
				posx=e.clientX+document.documentElement.scrollLeft;
				posy=e.clientY+document.documentElement.scrollTop;
			} else {
				posx=e.clientX+document.body.scrollLeft;
				posy=e.clientY+document.body.scrollTop;
			}
		}
		this.mousePos = {x:posx , y:posy};

	},
	
	// redirects the browser to given url
	// if frame!=false, it will open in provided frame (or new win if _blank)
	redirect: function (u, frame) {
		if (!frame) {
			document.location = u;  
		} else {
			if (frame=="_blank") {
				w = window.open(u, 'w');
			} else {
				window.frames[frame].document.location = u;
			}
		}
	},
	
	// performs a request - ajax style
	request: function (url, callBack) {
		if (window.XMLHttpRequest) { // native XMLHttpRequest
			this.req = new XMLHttpRequest();
			this.req.onreadystatechange =  callBack; 
			this.req.open("GET", url, true);
			this.req.send(null);
		} else if (window.ActiveXObject) { // The M$ 'standard'
			this.req = new ActiveXObject("Microsoft.XMLHTTP");
			if (this.req) { 
				this.req.onreadystatechange =   callBack;
				this.req.open("GET", url, true);
				this.req.send();
			}
		}
	},
	
	loadImage: function (url) {
		var img = new Image();
		img.src = url; 
		img.className = "rcImage";
		this.images[url] = img;
		return img;
	},
	
	detectBrowser: function() {
		var ua = navigator.userAgent.toUpperCase();
		var up = navigator.platform.toUpperCase().substr(0,3);
		var isSafari  = (ua.indexOf("SAFARI"   )>0);
		var isKHTML   = (ua.indexOf("KONQUEROR")>0 || isSafari);
		var isFirefox = (ua.indexOf("FIREFOX"  )>0); 
		var isOpera   = (ua.indexOf("OPERA"    )>=0);
		return { safari: isSafari, khtml: isKHTML, opera: isOpera, firefox: isFirefox, platform: up }
	},
	
	getWindowWidth: function()
	{
        var x = 0;
        if (self.innerHeight)
        {
                x = self.innerWidth;
        }
        else if (document.documentElement && document.documentElement.clientHeight)
        {
                x = document.documentElement.clientWidth;
        }
        else if (document.body)
        {
                x = document.body.clientWidth;
        }
        return x;
	}

};