Source: index.js

/**
 * @module DropdownMenu
 * @description Creates a drop down menu in your javaScript code.
 * 
 * @author [Andrej Hristoliubov]{@link https://anhr.github.io/AboutMe/}
 *
 * @copyright 2011 Data Arts Team, Google Creative Lab
 *
 * @license under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 */

import loadScript from '../loadScriptNodeJS/loadScript.js';

var optionsStyle = {

	//style rel="stylesheet"
	tag: 'style'

}

//Thanks to https://stackoverflow.com/a/27369985/5175935
//Такая же функция есть в frustumPoints.js но если ее использовать то она будет возвращать путь на frustumPoints.js
const getCurrentScript = function () {

	if ( document.currentScript && ( document.currentScript.src !== '' ) )
		return document.currentScript.src;
	const scripts = document.getElementsByTagName( 'script' ),
		str = scripts[scripts.length - 1].src;
	if ( str !== '' )
		return src;
	//Thanks to https://stackoverflow.com/a/42594856/5175935
	return new Error().stack.match( /(https?:[^:]*)/ )[0];

};
//Thanks to https://stackoverflow.com/a/27369985/5175935
const getCurrentScriptPath = function () {
	const script = getCurrentScript(),
		path = script.substring( 0, script.lastIndexOf( '/' ) );
	return path;
};
//console.warn( 'getCurrentScriptPath = ' + getCurrentScriptPath() );
var currentScriptPath = getCurrentScriptPath();
//Для D:\My documents\MyProjects\webgl\three.js\GitHub\commonNodeJS\master\DropdownMenu\build
var arrayPath = currentScriptPath.split( /(.*)(\/build)/ );
if ( arrayPath[2] === '/build' ) currentScriptPath = arrayPath[1];
//Для D:\My documents\MyProjects\webgl\three.js\GitHub\commonNodeJS\master\canvasMenu\build
arrayPath = currentScriptPath.split( /(.*)(\/canvasMenu)/ );
if ( arrayPath[2] === '/canvasMenu' ) currentScriptPath = arrayPath[1] + '/DropdownMenu';

//Attention! Load menu.css file before other css files for correctly priority of the styles
loadScript.sync( currentScriptPath + '/styles/menu.css', optionsStyle );//move dat.gui into canvas
loadScript.sync( currentScriptPath + '/styles/gui.css', optionsStyle );//move dat.gui into canvas
loadScript.sync( currentScriptPath + '/styles/Decorations/transparent.css', optionsStyle );
loadScript.sync( currentScriptPath + '/styles/Decorations/gradient.css', optionsStyle );
/*
loadScript.sync( 'https://raw.githack.com/anhr/DropdownMenu/master/styles/menu.css', optionsStyle );
loadScript.sync( 'https://raw.githack.com/anhr/DropdownMenu/master/styles/Decorations/transparent.css', optionsStyle );
loadScript.sync( 'https://raw.githack.com/anhr/DropdownMenu/master/styles/Decorations/gradient.css', optionsStyle );
*/

/**
 * Creates new menu
 * @param {Array} arrayMenu array of menu and submenu items. If array item is string then menu item name. If array item is object then options of the new menu item:
 * @param {String|HTMLElement} [arrayMenu.name] if string then menu item name. If HTMLElement then item element.
 * @param {String} [arrayMenu.title] menu item title.
 * @param {String} [arrayMenu.id] menu item identifier.
 * @param {String} [arrayMenu.style] menu item style. Example: "float: right;".
 * @param {Array} [arrayMenu.items] array of submenu items. Same as menu item.
 * @param {Function} [arrayMenu.onclick] <b>function(event)</b> called when user has clicked a menu item. event - event details.
 * @param {Object} [arrayMenu.drop] direction of drop of the submenu.
 * <pre>
 * Following directions is available:
 * If string then "up" - drop submenu to up. "left" - shift submenu to left.
 * If object then following members is available:
 * </pre>
 * @param {boolean} [arrayMenu.drop.up] true - drop submenu to up.
 * @param {boolean} [arrayMenu.drop.left] true - shift submenu to left.
 * @param {boolean} [arrayMenu.radio] true - defines a radio menu item.
 * @param {boolean} [arrayMenu.checkbox] true - defines a checkbox menu item.
 * @param {boolean} [arrayMenu.checked] true - checked state of a checkbox or radio menu item.
 *
 * @param {Object} [options] the following options are available.
 * @param {HTMLElement} [options.elParent="body" element] Parent element of new menu.
 * @param {HTMLElement} [options.canvas] <b>canvas</b> element. Use if you want put a menu inside a canvas. Use [CanvasMenu]{@link https://github.com/anhr/commonNodeJS/tree/master/canvasMenu} instead <b>DropdownMenu</b> for it.
 * @param {String} [options.decorations] You can decorate your menu by a built-in style or use your custom style. Currently two built-in styles is available:
 * <pre>
 * 'Gradient' - use [gradient.css]{@link https://raw.githack.com/anhr/commonNodeJS/master/DropdownMenu/styles/Decorations/gradient.css} file for decoration.
 * 'Transparent' - use [transparent.css]{@link https://raw.githack.com/anhr/commonNodeJS/master/DropdownMenu/styles/Decorations/transparent.css} file for decoration.
 * Custom decoration:
 * 'Custom' please edit the [custom.css]{@link https://raw.githack.com/anhr/commonNodeJS/master/DropdownMenu/Examples/html/custom.css} file from my example if you want a custom decoration of your menu.
 * <pre>
 */
function create( arrayMenu, options ) {

	options = options || {};
	options.elParent = options.elParent || document.querySelector( 'body' );
	switch ( options.decorations ) {

		case 'Gradient':
		case 'Transparent':
		case 'Custom':
			break;
		default: console.error( 'DropdownMenu.create: Invalid options.decorations: ' + options.decorations );
	}

	//create menu element
	var elMenu = document.createElement( 'menu' );
	if ( options.elParent.classList.contains( "container" ) )
		elMenu.className = 'controls';

	var timeoutControls;
	function displayControls() {

		elMenu.style.opacity = 1;
		clearTimeout( timeoutControls );
		timeoutControls = setTimeout( function () {

			elMenu.style.opacity = 0;

		}, 5000 );

	}
	if ( options.canvas ) {

		elMenu.style.opacity = 0;
		options.canvas.onmouseout = function ( event ) {

			elMenu.style.opacity = 0;

		}
		options.canvas.onmousemove = function ( event ) {

			displayControls();

		}
		elMenu.onmousemove = function ( event ) {

			displayControls();

		}

	}
	options.elParent.appendChild( elMenu );

	arrayMenu.forEach( function ( menuItem ) {

		var dropdownChild = 'dropdown-child';
		function moveUpLeft( drop ) {

			setTimeout( function () {

				var display = elDropdownChild.style.display;
				elDropdownChild.style.display = 'block';
				if ( drop.up )
					elDropdownChild.style.top = '-' + ( elDropdownChild.offsetHeight/* + borderWidth*/ ) + 'px';
				else elDropdownChild.style.top = ( elMenuButton.offsetHeight/* - borderWidth + top*/ -1 ) + 'px';
				if ( drop.left )
					elDropdownChild.style.left = ( elMenuButton.offsetWidth - elDropdownChild.offsetWidth/* - borderWidth*/ ) + 'px';
				elDropdownChild.style.display = display;//'none';

			}, 0 );

		}


		//Create menuButton class element
		var elMenuButton = document.createElement( 'span' );
		elMenuButton.className =
			'menuButton' + ( options.decorations === undefined ? '' : ' menuButton' + options.decorations );

		if ( menuItem.style !== undefined )
			elMenuButton.style.cssText = menuItem.style;

		if ( menuItem.radio !== undefined )
			elMenuButton.style.cssText = menuItem.style;

		if ( menuItem.onclick !== undefined )
			elMenuButton.onclick = menuItem.onclick;
		if ( menuItem.id !== undefined )
			elMenuButton.id = menuItem.id;

		var name;
		if ( typeof menuItem === 'string' )
			name = menuItem;
		else {

			name = menuItem.name;

			if ( menuItem.id )
				elMenuButton.id = menuItem.id;
			if ( menuItem.title )
				elMenuButton.title = menuItem.title;

		}

		//Create name span
		switch ( typeof name ) {
			case "object":
				elMenuButton.appendChild( name );
				break;
			case "string":
			case "undefined":
				elMenuButton.innerHTML = name;
				break;
			default: console.error( 'Invalid typeof name: ' + typeof name );
		}

		//Create dropdown-child items
		if ( menuItem.items ) {

			var elDropdownChild = document.createElement( 'span' );
			elDropdownChild.className = dropdownChild + ' ' + dropdownChild + ( options.decorations === undefined ? 'Default' : options.decorations );
			elDropdownChild.title = '';
			elMenuButton.appendChild( elDropdownChild );

			//for compatibility with firefox
//			getMenuButtonBorderWidth();

			menuItem.items.forEach( function ( itemItem ) {

				//Create name span
				var elName = document.createElement( 'nobr' ),
					classChecked = 'checked';
				function getItemName(item) {

					var str = typeof item === 'string' ?
						item :
							item.radio === true ?
							( item.checked ? '◉' : '◎' ) + ' ' + item.name
							: item.checkbox === true ? ( item.checked ? '☑' : '☐' ) + ' ' + item.name : item.name;//✓🗹
					//console.log(str);
					return str;

				}
				function getElementFromEvent( event ) {
					if ( !event ) event = window.event;//for IE6
					return event.target || event.srcElement;
				}
				var name;
				if ( typeof itemItem === 'string' )
					name = itemItem;
				else {

					name = itemItem.name;
					elName.onclick = function ( event ) {

						if ( itemItem.radio === true ) {

//							console.log( 'radio onclick ' + elName.innerHTML );
							menuItem.items.forEach( function ( item ) {

								if ( item.radio === true ) {

									if ( getElementFromEvent( event ) === item.elName ) {

										item.checked = true;
										item.elName.classList.add( classChecked );

									} else {

										item.checked = false;
										item.elName.classList.remove( classChecked );

									}
									item.elName.innerHTML = getItemName( item );

								}

							} );

						} else if ( itemItem.checkbox === true ) {

//							console.log( 'checkbox onclick ' + elName.innerHTML );
							if ( itemItem.checked === true ) {

								itemItem.elName.classList.add( classChecked );

							} else {

								itemItem.elName.classList.remove( classChecked );

							}
							itemItem.checked = !itemItem.checked;
							itemItem.elName.innerHTML = getItemName( itemItem );

						}
						if ( itemItem.onclick )
							itemItem.onclick( event );
							
					}

				}
				if ( itemItem.radio === true )
					elName.classList.add( 'radio' );
				if ( itemItem.checkbox === true )
					elName.classList.add( 'checkbox' );
				if ( itemItem.id )
					elName.id = itemItem.id;
				if ( itemItem.title )
					elName.title = itemItem.title;
				elName.innerHTML = getItemName( itemItem );

				if ( itemItem.checked === true )
					elName.classList.add( classChecked );

				elDropdownChild.appendChild( elName );
				if ( typeof itemItem !== "string")
					itemItem.elName = elName;

			} );

			if ( typeof menuItem.drop === 'object' ) {

				moveUpLeft( menuItem.drop );

			} else {

				switch ( menuItem.drop ) {

					case 'up':
						moveUpLeft( {

							up: true,

						} );
						break;
					case 'left':
						moveUpLeft( {

							left: true,

						} );
						break;
					case undefined:
						setTimeout( function () {

							elDropdownChild.style.left = '-' + elMenuButton.clientWidth + 'px';
							elDropdownChild.style.top = ( elMenuButton.offsetHeight/* - getMenuButtonBorderWidth()*/ - 1 ) + 'px';

						}, 0 );
						break;
					default: console.error( 'Invalid menuItem.drop: ' + menuItem.drop );

				}

			}

		}

		elMenu.appendChild( elMenuButton );

	} );
	return elMenu;

}
export { create };
//export default create;