// ==UserScript==
// @name         MyContextMenu
// @description   http://https://wish123.cnblogs.com/?MyContextMenu
// @version      1.0
// @description  原生js右键弹出菜单
// @author       Wilson
// @license      MIT
// modify by https://github.com/electerious/basicContext/
(function() {
  if(document.querySelector("#myContextMenuStyle")) {
    return;
  }
  let style = `
<style id="myModalStyle">
/* base css */
.basicContext,
.basicContext * {
  box-sizing: border-box;
}
.basicContextContainer {
  position: fixed;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  z-index: 1000;
  -webkit-tap-highlight-color: transparent;
}
.basicContext {
  position: absolute;
  opacity: 0;
  -moz-user-select: none;
  -webkit-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
.basicContext__item {
  cursor: pointer;
}
.basicContext__item--separator {
  float: left;
  width: 100%;
  height: 1px;
  cursor: default;
}
.basicContext__item--disabled {
  cursor: default;
}
.basicContext__data {
  min-width: 140px;
  padding-right: 20px;
  text-align: left;
  white-space: nowrap;
}
.basicContext__icon {
  display: inline-block;
}
.basicContext--scrollable {
  height: 100%;
  -webkit-overflow-scrolling: touch;
  overflow-y: auto;
}
.basicContext--scrollable .basicContext__data {
  min-width: 160px;
}
/* default theme css */
.basicContext {
  padding: 6px;
  background-color: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 0 1px rgba(0, 0, 0, 0.2);
  border-radius: 3px;
}
.basicContext__item {
  margin-bottom: 2px;
}
.basicContext__item--separator {
  margin: 4px 0;
  background-color: rgba(0, 0, 0, 0.1);
}
.basicContext__item--disabled {
  opacity: 0.5;
}
.basicContext__item:last-child {
  margin-bottom: 0;
}
.basicContext__data {
  padding: 6px 8px;
  color: #333;
  border-radius: 2px;
}
.basicContext__item:not(.basicContext__item--disabled):hover
  .basicContext__data {
  color: #fff;
  background-color: #4393e6;
}
.basicContext__item:not(.basicContext__item--disabled):active
  .basicContext__data {
  background-color: #1d79d9;
}
.basicContext__icon {
  margin-right: 10px;
  width: 12px;
  text-align: center;
}
/* [自定义] 自定义css样式 */
.basicContextContainer{
    width: auto;
    height: auto;
}
.basicContext {
    padding: 0px;
    min-width: 150px;
    height: auto;
    background-color: rgba(241, 240, 240, 0.96);
}
.basicContext table{
    padding: 4px 0;
    width: 100%;
}
.basicContext__item {
    margin-bottom: 0px;
}
.basicContext__data {
    padding: 2px 12px;
    border-radius:0px;
    font-size: 13.8px;
    color: #3a383a;
}
.basicContext__item--separator {
    margin: 2px 0;
}
.basicContext__item:not(.basicContext__item--disabled):hover .basicContext__data {
    background-color: #4d92f8;
}
</style>
  `
  document.body.insertAdjacentHTML("beforeend", style);
})();
"use strict";
!(function (basicContext, callback) {
  "undefined" != typeof module && module.exports
    ? (module.exports = callback())
    : "function" == typeof define && define.amd
    ? define(callback)
    : (window[basicContext] = callback());
})("basicContext", function () {
let overflow = null
const ITEM      = 'item',
      SEPARATOR = 'separator'
const dom = function(elem = '') {
	return document.querySelector('.basicContext ' + elem)
}
const valid = function(item = {}) {
	let emptyItem = (Object.keys(item).length===0 ? true : false)
	if (emptyItem===true)     item.type    = SEPARATOR
	if (item.type==null)      item.type    = ITEM
	if (item.class==null)     item.class   = ''
	if (item.visible!==false) item.visible = true
	if (item.icon==null)      item.icon    = null
	if (item.title==null)     item.title   = 'Undefined'
	// Add disabled class when item disabled
	if (item.disabled!==true) item.disabled = false
	if (item.disabled===true) item.class += ' basicContext__item--disabled'
	// Item requires a function when
	// it's not a separator and not disabled
	if (item.fn==null && item.type!==SEPARATOR && item.disabled===false) {
		console.warn(`Missing fn for item '${ item.title }'`)
		return false
	}
	return true
}
const buildItem = function(item, num) {
	let html = '',
	    span = ''
	// Parse and validate item
	if (valid(item)===false) return ''
	// Skip when invisible
	if (item.visible===false) return ''
	// Give item a unique number
	item.num = num
	// Generate span/icon-element
	if (item.icon!==null) span = `<span class='basicContext__icon ${ item.icon }'></span>`
	// [自定义]
	item.extAttr = item.extAttr || ""
	// Generate item
	if (item.type===ITEM) {
		html = `
		       <tr class='basicContext__item ${ item.class }'>
		           <td class='basicContext__data' data-num='${ item.num }' ${item.extAttr}>${ span }${ item.title }</td>
		       </tr>
		       `
	} else if (item.type===SEPARATOR) {
		html = `
		       <tr class='basicContext__item basicContext__item--separator'></tr>
		       `
	}
	return html
}
const build = function(items) {
	let html = ''
	html += `
	        <div class='basicContextContainer'>
	            <div class='basicContext'>
	                <table cellspacing="0">
	                    <tbody>
	        `
	items.forEach((item, i) => html += buildItem(item, i))
	html += `
	                    </tbody>
	                </table>
	            </div>
	        </div>
	        `
	return html
}
const getNormalizedEvent = function(e = {}) {
	let pos = {
		x : e.clientX,
		y : e.clientY
	}
	if (e.type==='touchend' && (pos.x==null || pos.y==null)) {
		// We need to capture clientX and clientY from original event
		// when the event 'touchend' does not return the touch position
		let touches = e.changedTouches
		if (touches!=null&&touches.length>0) {
			pos.x = touches[0].clientX
			pos.y = touches[0].clientY
		}
	}
	// Position unknown
	if (pos.x==null || pos.x < 0) pos.x = 0
	if (pos.y==null || pos.y < 0) pos.y = 0
	return pos
}
const getPosition = function(e, context) {
	// Get the click position
	let normalizedEvent = getNormalizedEvent(e)
	// Set the initial position
	let x = normalizedEvent.x,
	    y = normalizedEvent.y
	// Get size of browser
	let browserSize = {
		width  : window.innerWidth,
		height : window.innerHeight
	}
	// Get size of context
	let contextSize = {
		width  : context.offsetWidth,
		height : context.offsetHeight
	}
	// Fix position based on context and browser size
	if ((x + contextSize.width) > browserSize.width)   x = x - ((x + contextSize.width) - browserSize.width)
	if ((y + contextSize.height) > browserSize.height) y = y - ((y + contextSize.height) - browserSize.height)
	// Make context scrollable and start at the top of the browser
	// when context is higher than the browser
	if (contextSize.height > browserSize.height) {
		y = 0
		context.classList.add('basicContext--scrollable')
	}
	// Calculate the relative position of the mouse to the context
	let rx = normalizedEvent.x - x,
	    ry = normalizedEvent.y - y
	return { x, y, rx, ry }
}
const bind = function(item = {}) {
	if (item.fn==null)        return false
	if (item.visible===false) return false
	if (item.disabled===true) return false
	dom(`td[data-num='${ item.num }']`).onclick       = item.fn
	dom(`td[data-num='${ item.num }']`).oncontextmenu = item.fn
	return true
}
const show = function(items, e, fnClose, fnCallback) {
    //[自定义] delete old menu
    let basicContextContainer = document.querySelector('.basicContextContainer');
    if(basicContextContainer){
        basicContextContainer.remove();
    }
	// Build context
	let html = build(items)
	// Add context to the body
	document.body.insertAdjacentHTML('beforeend', html)
	// Save current overflow and block scrolling of site
	if (overflow==null) {
		overflow = document.body.style.overflow
		document.body.style.overflow = 'hidden'
	}
	// Cache the context
	let context = dom()
	// Calculate position
	let position = getPosition(e, context)
	// Set position
	context.style.left            = `${ position.x }px`
	context.style.top             = `${ position.y }px`
	context.style.transformOrigin = `${ position.rx }px ${ position.ry }px`
	context.style.opacity         = 1
	// Close fn fallback
	if (fnClose==null) fnClose = close
	// Bind click on background
	context.parentElement.onclick       = fnClose
	context.parentElement.oncontextmenu = fnClose
	// Bind click on items
	items.forEach(bind)
	// Do not trigger default event or further propagation
	if (typeof e.preventDefault === 'function')  e.preventDefault()
	if (typeof e.stopPropagation === 'function') e.stopPropagation()
	// Call callback when a function
	if (typeof fnCallback === 'function') fnCallback()
	return true
}
const visible = function() {
	let elem = dom()
	if (elem==null || elem.length===0) return false
	else                               return true
}
const close = function() {
	if (visible()===false) return false
	let container = document.querySelector('.basicContextContainer')
	container.parentElement.removeChild(container)
	// Reset overflow to its original value
	if (overflow!=null) {
		document.body.style.overflow = overflow
		overflow = null
	}
	return true
}
return {
	ITEM,
	SEPARATOR,
	show,
	visible,
	close
}
});
//[自定义] 解决basicContextContainer出现系统菜单的bug
document.addEventListener('click', function(e){
    if(basicContext) basicContext.close();
});
document.addEventListener('contextmenu', function(e){
    if(['basicContextContainer','basicContext'].indexOf(e.target.className)!==-1) {
        e.preventDefault();
        return false;
    }
});
//使用示例
// const clicked = function(e) {
//     console.log(e.target.innerHTML);
// }
// document.querySelector('.my-context-menu-btn').addEventListener('contextmenu', function(e){
//     const items = [
//         { title: '新标签打开链接', extAttr: "data-name='new-blank'", fn: clicked },
//         { },
//         { title: '复制链接地址', extAttr: "data-name='copy-link'", fn: clicked },
//         { title: '复制选中的文本', extAttr: "data-name='copy-text'", fn: clicked, disabled: true },
//         { title: '复制响应数据', extAttr: "data-name='copy-response'", fn: clicked},
//         { },
//         { title: '复制为cURL格式', extAttr: "data-name='copy-curl'", fn: clicked},
//         { title: '复制为fetch格式', extAttr: "data-name='copy-fetch'", fn: clicked},
//         { title: '复制为await格式', extAttr: "data-name='copy-await'", fn: clicked},
//         { title: '复制为xhr格式', extAttr: "data-name='copy-xhr'", fn: clicked},
//         { title: '复制为分享链接', extAttr: "data-name='copy-share'", fn: clicked},
//         { },
//         { title: '删除该请求', extAttr: "data-name='del-request'", fn: clicked},
//         { title: '删除所有请求', extAttr: "data-name='del-all-request'", fn: clicked }
//     ]
//     basicContext.show(items, e);
// });