// SpryMenuBarIEWorkaroundsPlugin.js - version 0.8 - Spry Pre-Release 1.7
//
// Copyright (c) 2010. Adobe Systems Incorporated.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of Adobe Systems Incorporated nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

(function() { // BeginSpryComponent

if (typeof Spry == "undefined" || !Spry.Widget || !Spry.Widget.MenuBar2)
{
	alert("SpryMenuBarIEWorkaroundsPlugin.js requires SpryMenu.js!");
	return;
}

var isIE6 = (window.ActiveXObject && !window.XMLHttpRequest);
if (isIE6)
	Spry.Utils.addClassName(document.documentElement, "SpryIsIE6");
var isIE = window.ActiveXObject ? true : false;

// This plugin attempts to workaround the following problems with IE6, IE7 and IE8:
//
// A. Submenus render underneath <select> elements (IE6).
//
//    - When the plugin is enabled, it will automatically insert transparent iframes beneath each
//      subMenu. This has the effect of forcing the subMenu to render above elements like <select> that
//      use native windows.
//
// B. Multiple class selectors are not supported (IE6).
//
//    - IE6 does not support selectors that make use of multiple class names on a single element.
//      For example a selector of the form .MenuItemWithSubMenu.MenuItemHover will be interpreted
//      by IE6 as simply .MenuItemHover. To workaround this problem set the 'useCombinedClassNames'
//      option for the plugin (true by default) which will generate combined class names such as
//      .MenuItemWithSubMenuHover, .MenuItemFirstHover, .MenuItemFirstWithSubMenuHover, etc. Class
//      names generated by this plugin are as follows:
//
//          MenuItemFirstWithSubMenu
//          MenuItemLastWithSubMenu
//          MenuItemFirstHover
//          MenuItemLastHover
//          MenuItemWithSubMenuHover
//          MenuItemFirstWithSubMenuHover
//          MenuItemLastWithSubMenuHover
//
// C. Elements with position values other than 'static' establish a new z-index context (IE6).
//
//    - This problem manifests itself when subMenus overlap. You will see subMenus rendering
//      behind menu items that come after the subMenu's parent menu item. The solution for this
//      is to set the z-index of the entire subMenu parent element hierarchy such that they are
//      higher than any of their siblings. By default, the plugin will place a z-index of 1000
//      on all parents of the currentSubMenu. You can change the z-index value by passing the
//      'miContainerZIndex' option with the desired z-index integer value. If you would prefer
//      to control the z-index via a CSS rule, set the 'miContainerHoverZIndex' option to undefined,
//      and then use the class 'MenuItemContainerHover' in your stylesheet to set the desired
//      z-index value.
//
//      You can read more about this problem here:
//
//          http://www.adobe.com/cfusion/communityengine/index.cfm?event=showdetails&postId=829&productId=1&loc=en_US
//
// D. <li> elements that contain <a> elements render with gaps below them. (IE6-IE8)
//
//    - This one is a doozy to work around. For menu items with no subMenus, you can get rid of gaps
//      by using this CSS trick:
//
//          http://www.adobe.com/cfusion/communityengine/index.cfm?event=showdetails&postId=824&productId=1&loc=en_US
//
//      But the trick above does not work for menu items with sub menus since once they become
//      visible, IE6 adds the gap. There are all sorts of tricks that work in specific scenarios,
//      that involve setting the <li> to inline and floating, etc, etc. But that doesn't seem to work
//      in every styling case/situation. IE8 is especially sneaky, since the problem seems to be
//		sensitive to the specific characters in the whitespace too, so can crop up after seemingly innocuous changes.
//		The only fool-proof way to get rid of this problem is to get rid of the <ul> and <li> elements 
//		in the generated markup for the menubar.
//
//      Getting rid of <ul>/<li> elements is the default behavior for this plugin. If you really want to deal
//      with the headache/IE Hacks necessary to get rid of the gaps, you can turn this behavior off by setting
//      the 'useDivs' option to false. If 'useDivs' is true, you will not have to use any tricks/hacks in your
//      CSS to get rid of gaps.
//
// One other bug that you may see, while styling your menubar, that is *NOT* covered by this plugin is when
// <a> tags are modified to be display:block, sometimes they are not clickable unless you click over the text.
// this problem can be solved by using one of the tricks mentioned here:
//
//    http://www.adobe.com/cfusion/communityengine/index.cfm?event=showdetails&postId=1643&productId=1&loc=en_US

var IEWP = Spry.Widget.MenuBar2.IEWorkaroundsPlugin = {
	config: {
		pluginOptionsProp:      "IEWP",
		enablePlugin:           undefined, // true or false, if undefined, defaults to true for IE6-IE8
		stripWhiteSpace:        false,
		useDivs:                undefined, // defaults to true for all IE, false for other browsers.
		useCombinedClassNames:  undefined,	// defaults to true for IE6, false for other IE and other browsers
		miContainerZIndex:      undefined,
		miContainerHoverZIndex: 1000
	},

	initialize: function(mb)
	{
		var opts = mb.setOptions({}, IEWP.config);

		if (mb[opts.pluginOptionsProp])
			mb.setOptions(opts, mb[opts.pluginOptionsProp]);
		
		opts.useDivs = (opts.useDivs != undefined) ? opts.useDivs : isIE;
		opts.useCombinedClassNames = (opts.useCombinedClassNames != undefined) ? opts.useCombinedClassNames : isIE6;

		mb[opts.pluginOptionsProp] = opts;

		// If the enabled property is undefined, we will automatically
		// enable the plugin if running on IE.

		var enabled = (opts.enablePlugin != undefined) ? opts.enablePlugin : isIE;
		if (enabled)
			mb.addObserver(this);
	},

	getOptions: function(mb)
	{
		return mb[IEWP.config.pluginOptionsProp];
	},

	getMenuItemContainer: function(mb, sm)
	{
		return sm ? Spry.Utils.getAncestor(sm, "." + mb.menuItemContainerClass) : null;
	},

	showSubMenuIFrame: function(mb, sm)
	{
		if (!sm.ieIFrame)
		{
			var iframe = document.createElement("iframe");
			iframe.tabIndex = '-1';
			iframe.src = 'javascript:""';
			iframe.frameBorder = '0';
			iframe.scrolling = 'no';
			iframe.style.opacity = "0.01";
			iframe.style.filter = "alpha(opacity=1)";
			iframe.style.position = "absolute";
			sm.ieIFrame = iframe;
		}
	
		var iframe = sm.ieIFrame;
		iframe.style.top = sm.offsetTop + "px";
		iframe.style.left = sm.offsetLeft + "px";
		iframe.style.width = sm.offsetWidth + "px";
		iframe.style.height = sm.offsetHeight + "px";
		sm.parentNode.insertBefore(iframe, sm);
	},

	hideSubMenuIFrame: function(mb, sm)
	{
		if (sm)
		{
			var iframe = sm.ieIFrame;
			if (iframe)
			{
				var p = iframe.parentNode;
				if (p) p.removeChild(iframe);
			}
		}
	},

	stripTextNodes: function(ele)
	{
		var c = ele.firstChild;
		while (c)
		{
			var n = c;
			c = c.nextSibling;
			if (n.nodeType == 3) // Node.TEXT_NODE
				ele.removeChild(n);
		}
	},

	getMenuItemClasses: function(mb, mi)
	{
		// MenuItem[First|Last]?[WithSubMenu]?[Hover]?

		var results = [];

		var posArr = [ "" ];
		if (mb.hasClassName(mi, mb.menuItemFirstClass))
			posArr.push("First");
		if (mb.hasClassName(mi, mb.menuItemLastClass))
			posArr.push("Last");

		var hasSubMenu = mb.hasClassName(mi, mb.menuItemWithSubMenuClass);

		var miClass = mb.menuItemClass;

		for (var i = 0; i < posArr.length; i++)
		{
			var c = miClass + posArr[i];
			results.push(c);
			if (hasSubMenu)
				results.push(c + "WithSubMenu");
		}

		return results;
	},

	getMenuItemHoverClasses: function(mb, mi)
	{
		var results = IEWP.getMenuItemClasses(mb, mi);
		for (var i = 0; i < results.length; i++)
			results[i] += "Hover";
		return results;
	},

	onPostTransformMarkup: function(mb, evt)
	{
		var opts = IEWP.getOptions(mb);

		if (opts.stripWhiteSpace)
		{
			var uls = Spry.$$("ul", mb.element);
			for (var i = 0; i < uls.length; i++)
			{
				var ul = uls[i];
				IEWP.stripTextNodes(ul);
				var eles = mb.getElementChildren(ul);
				for (var j = 0; j < eles.length; j++)
					IEWP.stripTextNodes(eles[j]);
			}
		}

		// IE6 has some issues with gaps between menu items. We can easily fix
		// menu items with no submenus by either stripping the whitespace in and
		// around <ul> and <li> elements, or we can use a zoom:1 property on the <li>
		// elements, but that doesn't fix the gap problem when dealing with menu items
		// that have submenus. To work around this and ease the styling hacks/pains, we
		// can also convert ul/li elements to divs.

		if (opts.useDivs)
		{
			var uls = Spry.$$("ul", mb.element);
			for (var i = 0; i < uls.length; i++)
			{
				var ul = uls[i];
				var div = document.createElement("div");
				if (ul.id) div.id = div.id;
				if (ul.className) div.className = ul.className;
				var children = [];
				var c = ul.firstChild;
				while (c)
				{
					var n = c;
					if (n.nodeName.toLowerCase() == "li")
					{
						n = document.createElement("div");
						if (c.id) n.id = c.id;
						if (c.className) n.className = c.className;
						mb.appendChildNodes(n, mb.extractChildNodes(c));
					}
					children.push(n);
					c = c.nextSibling;
				}
				var p = ul.parentNode;
				p.insertBefore(div, ul);
				p.removeChild(ul);
				mb.appendChildNodes(div, children);
			}
		}

		if (opts.useCombinedClassNames)
		{
			var mis = Spry.$$("." + mb.menuItemClass, mb.element);
			for (var i = 0; i < mis.length; i++)
			{
				var mi = mis[i];
				var classes = IEWP.getMenuItemClasses(mb, mi);
				for (var j = 0; j < classes.length; j++)
					mb.addClassName(mi, classes[j]);
			}
		}
	},

	onPostAddHoverClass: function(mb, evt)
	{
		var opts = IEWP.getOptions(mb);
		if (!opts.useCombinedClassNames)
			return;

		var mi = evt.menuItem;
		var classNames = IEWP.getMenuItemHoverClasses(mb, mi);
		for (var i = 0; i < classNames.length; i++)
			mb.addClassName(mi, classNames[i]);
	},

	onPostRemoveHoverClass: function(mb, evt)
	{
		var opts = IEWP.getOptions(mb);
		if (!opts.useCombinedClassNames)
			return;

		var mi = evt.menuItem;
		var classNames = IEWP.getMenuItemHoverClasses(mb, mi);
		for (var i = 0; i < classNames.length; i++)
			mb.removeClassName(mi, classNames[i]);
	},

	onPreHideSubMenuHierarchy: function(mb, evt)
	{
		var smh = evt.subMenus;
		if (smh && smh.length)
		{
			var opts = IEWP.getOptions(mb);

			for (var i = 0; i < smh.length; i++)
			{
				IEWP.hideSubMenuIFrame(mb, smh[i]);
				if (opts.miContainerHoverZIndex)
				{
					var mic = IEWP.getMenuItemContainer(mb, smh[i]);
					if (mic)
						mic.style.zIndex = (opts.miContainerZIndex ? miContainerZIndex : "" ) + "";
				}
			}
		}
	},

	onPostShowSubMenuHierarchy: function(mb, evt)
	{
		var smh = evt.subMenus;
		if (smh && smh.length)
		{
			var opts = IEWP.getOptions(mb);
			for (var i = 0; i < smh.length; i++)
			{
				IEWP.showSubMenuIFrame(mb, smh[i]);
				if (opts.miContainerHoverZIndex)
				{
					var mic = IEWP.getMenuItemContainer(mb, smh[i]);
					if (mic)
						mic.style.zIndex = opts.miContainerHoverZIndex + "";
				}
			}
		}
	}
};

// We want to add our plugin to the default configuration for MenuBar2 if it is included!

Spry.Widget.MenuBar2.config.plugIns.push(Spry.Widget.MenuBar2.IEWorkaroundsPlugin);

})(); // EndSpryComponent


