Maxymiser task 16

by Vladimir

HTML

<h2>Задание</h2>
<p><strong>Выполнить</strong><br>Реализовать <tt>ContextMenu</tt><br /><u id="menuTarget">Меню тут</u></p>
<ul>
<li>первый аргумент - узел на котором будет работать контекстное меню</li>
<li>второй аргумент - описание структуры меню. Пример описания прикладывается. Структура может быть любой по вложенности и количеству элементов меню. (рекурсивно генерировать DOM будет ок)</li>
<li>подменю может содержать вложенные подменю</li>
<li>при правом клике по узлу, для которого было создано ContextMenu показывать меню прямо под местом клика.</li>
<li>при клике по пункту меню должна выполняться соответствующая функция</li>
<li>каждый элемент, содержащий подменю, должен быть отмечен "&gt;" символом</li>
<li>подменю открывается при наведении на пункт подменю, и закрывается при уходе мышки с подменю или пункта подменю (смотри события mouseenter и mouseleave)</li>
<li>с позиционированием (чтобы все меню и подменю вмещались в видимую часть экрана) можно не заморачиваться</li>
<li>стилизовать меню можно на свой вкус (главное - видимые границы элементов)</li>
</ul>
<code>var menuExample = [{
	title: 'File',
	action: function () {console.log('open file')}
}, {
	title: 'Edit',
	action: function () {console.log('edit content')}
}, {
	title: 'More stuff',
	submenu: [{
		title: 'Send by email',
		action: function () {console.log('emailed')}
	}, {
		title: 'Send via skype',
		action: function () {console.log('skyped')}
	}]
}]</code>

CSS

body {
    font-family: Verdana, sans-serif;
}
code {
    display: block;
    background: #EEE;
    white-space: pre;
    padding: 5px;
}

.context-menu,
.submenu {
	-webkit-box-shadow: 0 0 6px 1px #EEE;
	-moz-box-shadow: 0 0 6px 1px #EEE;
    box-shadow: 0 0 6px 1px #EEE;
	padding: 0px;
	list-style: none;
	position: absolute;
	display: none;
	background: #EEE;
	font-family: 'Lucida Console', Courier,monospace;
}
.context-menu li {
    cursor:pointer;
    padding: 3px;
    height:14px;
    line-height: 14px;
	width: 150px;
	position: relative;
}
.context-menu li:hover {
    background: #CCC; 
}
.context-menu li.have-items:after {
	content: '>';
	display: block;
	right: 2px;
	position: absolute;
	top: 2px;
}
.submenu {
	left: 100%;
	margin: -20px 0 0;
}

JavaScript

//* Cross-browser adding events
var addEvent, removeEvent;
 
if (document.addEventListener) { // проверка существования метода
	addEvent = function(elem, type, handler) {
		elem.addEventListener(type, handler, false);
	};
	removeEvent = function(elem, type, handler) {
		elem.removeEventListener(type, handler, false);
	};
} else {
	addEvent = function(elem, type, handler) {
		elem.attachEvent("on" + type, handler);
	};
	removeEvent = function(elem, type, handler) {
		elem.detachEvent("on" + type, handler);
	};
}

var menuDom;
var subMenus = [];
function ContextMenu(el, struct) {
	menuDom = document.createElement('ul');
	menuDom.className = 'context-menu';
	var subLevels = [];
	for(var i in struct) {
		var menuItem = document.createElement('li');
		var menuItemTitle = document.createTextNode(struct[i].title);
		menuItem.appendChild(menuItemTitle);
		if('action' in struct[i]) {
			// simple item
			addEvent(menuItem, 'click', struct[i].action);
		} else {
			// item with submenu
			menuItem.className = 'have-items';
			subMenus.push(buildSubmenu(struct[i].submenu));

			menuItem.appendChild(subMenus[subMenus.length-1]);
			addEvent(menuItem, 'mouseover', function() {
				this.children[0].style.display = 'block';			
			});
			addEvent(menuItem, 'mouseout', function() {
				this.children[0].style.display = 'none';
			});
		}
		menuDom.appendChild(menuItem);
	}
	document.body.appendChild(menuDom);
	addEvent(el, 'contextmenu', function(e) {
		var ev = e || window.event;
		ev.stopPropagation();
		ev.preventDefault();
		menuDom.style.display = 'block';
		menuDom.style.left = ev.pageX + 'px'; 
		menuDom.style.top = ev.pageY + 'px'; 
	});
	addEvent(document.body, 'click', menuHide);
}
//* NEED MORE REFACTORING
function buildSubmenu(submenu) {
	var subMenuDom = document.createElement('ul');
	subMenuDom.className = 'submenu';
	for(var i = 0; i < submenu.length; i++) {
		var subMenuItem =...