Bootstrap contextmenu

Extra lightweight plugin (4kB) for dynamically creating complex context menus in bootstrap3

<script src="https://code.jquery.com/jquery-2.2.4.min.js"
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
... and include plugin:
<script src="bootstrap-contextmenu.js"></script>
<link rel="stylesheet" type="text/css" href="bootstrap-contextmenu.css">		

2.1 Simple example

<div class="list-group menu1">
	<div class="list-group-item">Right click on me</div>
	<div class="list-group-item">Right click on me</div>		
</div>

<script type="text/javascript">
const menu = Object.create(contextMenu)
menu.init('.menu1 .list-group-item' , [
		{
			type : 'header',
			text : 'header in context menu',
		}, 
		{
			type : 'item', 
			text : 'hello'
		}, 
		{
			type : 'item', 
			text : 'world'
		},
		{
			type : 'divider'
		}, 
		{
			type : 'item', 
			text : 'other item under divider'
		}
	])
</script>	

2.2 Complex example with callbacks and nested menus

If you wanna display other options conditionally to clicked item (for example based on data-attributes or id), You can use callbacks in options:

<div class="list-group menu2">
	<div class="list-group-item" data-id="1">Right click on me</div>
	<div class="list-group-item" data-id="2">Right click on me</div>		
</div>

<script type="text/javascript">
const menu2 = Object.create(contextMenu)
menu2.init('.menu2 .list-group-item' , [
		{
			type : 'item', 
			text : function(e) {
				let element = $(e.currentTarget)
				return 'This item has data-id="'+element.data('id')+'"'
			},
		}, 
		{
			type : 'item', 
			text : function(e) {
				let element = $(e.currentTarget) 
				if(element.data('id') == 1) {
					return 'I am active!'
				} 

				return 'I am not active'
			}, 
			href : 'http://www.google.pl',
			attr : {
				class : function(e) {
					let element = $(e.currentTarget) 
					if(element.data('id') == 1) {
						return 'active'
					}
				}
			}
		}, 
		{
			type : 'item',
			text : 'this item should be visible only if target element has data-id = 2',
			display : function(e) {
				let element = $(e.currentTarget)
				if(element.data('id') == 2) {
					return true
				}
				return false
			}
		}, 
		{
			type : 'submenu', 
			text : 'submenu example',
			items : [
				{
					type : 'item', 
					text : 'submenu item 1',
				}, 
				{
					type : 'item', 
					text : function() {
						return 'random number '+ Math.ceil(Math.random()*1000)
					}
				}, 
				{
					type : 'submenu', 
					text : 'next level submenu',
					items : [
						{
							type : 'item', 
							text : 'subsubmenu item 1',
						}, 
						{
							type : 'item', 
							text : function() {
								return 'random number '+ Math.ceil(Math.random()*1000)
							}
						}, 
					]
				}
			]
		}
	])
</script>

Initialization requires 2 steps:
Create object from contextMenu object, and call init() function, which has two parameters: selector and items

selector string - any selector compatible with loaded version of jquery
options array - array of options determines how to render context menu

<script type="text/javascript">
const menuObject = Object.create(contextMenu)
menuObject.init(selector, options)
</script>	

4.1 Type

type string - determines type of bootstrap dropdown element. Available values: item, header, divider, submenu (recursive)
Example:
<script type="text/javascript">
const menuObject = Object.create(contextMenu)
menuObject.init(selector, [
		{
			type : 'header', 
			text : 'menu header'
		}, 
		{
			type : 'item',
			text : 'menu item'
		}
	])
</script>	

4.2 Text

text string|callback - text displayed inside HTML <a> tag. You can also inject HTML here if you wanna display icon or someyhing else.
Example:
<script type="text/javascript">
const menuObject = Object.create(contextMenu)
menuObject.init(selector, [
		{
			type : 'item', 
			text : 'Some text here'
		}, 
		{
			type : 'item',
			text : function(e) {
				let element = $(e.currentTarget)
				return element.text()
			}
		}
	])
</script>	

4.3 Display

display boolean|callback default true - if you wanna dynamically decide which element should have some extra options, or display other options based on some conditions, You can use this option as callback.
Example
<script type="text/javascript">
const menuObject = Object.create(contextMenu)
menuObject.init(selector, [
		{
			type : 'item', 
			text : 'Some text here'
		}, 
		{
			type : 'item',
			text : 'extra option', 
			display : function(e) {
				let element = $(e.currentTarget)
				if(element.hasClass('super-extra-element') === true) {
					return true
				}

				return false
			}
		}
	])
</script>

4.4 Html

html string|callback - If you want render <li> element by yourself - You can use this option
Notice:
1. This option is available only when You declared type as 'item'
2. All other options will be ignored
Example:
<script type="text/javascript">
const menuObject = Object.create(contextMenu)
menuObject.init(selector, [
		{
			type : 'item', 
			html : '<li><a>menu item</a></li>'
		}, 
		{
			type : 'item', 
			html : function(e) {
				let element = $(e.currentTarget)
				return '<li><a>'+element.text()+'</a></li>'
			}
		}
	])
</script>	

4.5 Href

href string|callback default: javascript:void(0) - value of href property inside link
Example:
<script type="text/javascript">
const menuObject = Object.create(contextMenu)
menuObject.init(selector, [
		{
			type : 'item', 
			text : 'link to google',
			href : 'http://www.google.pl'
		}, 
		{
			type : 'item', 
			text : 'open selected file', 
			href : function(e) {
				let element = $(e.currentTarget) 
				return '/files/preview/'+element.data('id')
			}
		}
	])
</script>	

4.6 Attr

attr object - list of attributes (eg. 'class', 'id', 'title', ...) which should be rendered inside <li> tag. Each of attribute can be string or callback.
Example:
<script type="text/javascript">
const menuObject = Object.create(contextMenu)
menuObject.init(selector, [
		{
			type : 'item', 
			text : 'hello world',
			attr : {
				title : 'some helper text',
				class : 'active'
			}
		}, 
		{
			type : 'item', 
			text : 'attributes as callbacks',
			attr : {
				class : function(e) {
					let element = $(e.currentTarget)
					if(element.data('my_data') == 'super') {
						return 'active'
					}
				}
			} 
		}
	])
</script>
	
submenu is recursice extra type of item - You can add large amount of submenus, but remember: More items, menu is less readable, and less handy - this is context menu - You should place here only most common options.

Example:
<script type="text/javascript">
const menuObject = Object.create(contextMenu)
menuObject.init(selector, [
		{
			type : 'item', 
			text : 'typical item'
		}, 
		{
			type : 'submenu', 
			text : 'submenu example',
			items : [
				{
					'type' : 'item', 
					'text' : 'submenu item 1'
				},
				{
					'type' : 'item', 
					'text' : 'submenu item 2'
				},
			]
		}
	])
</script>