205 lines
5.4 KiB
JavaScript
Executable File
205 lines
5.4 KiB
JavaScript
Executable File
(function ( win, doc, PKAE ) {
|
|
'use strict';
|
|
|
|
var activeMenu = [],
|
|
namespace = win,
|
|
contextStorage = {},
|
|
_id = 0;
|
|
|
|
var closeEvent = [ 'mousedown', 'touchup' ];
|
|
|
|
/**
|
|
* Goes through every single context instance and terminates
|
|
* it.
|
|
**/
|
|
var closeContext = function ( e, force ) {
|
|
if (!e) return ;
|
|
if (activeMenu.length === 0) return ;
|
|
|
|
var el = e.target || e.srcElement;
|
|
|
|
if (!el || el.className.indexOf('_action') === -1 || force)
|
|
{
|
|
var l = activeMenu.length;
|
|
while (l--) terminate (activeMenu[ l ]);
|
|
activeMenu = [];
|
|
}
|
|
},
|
|
/**
|
|
* Go through every children element of the context el,
|
|
* and remove all listeners and added attributes, then remove it
|
|
* from the dom also
|
|
**/
|
|
terminate = function( e ) {
|
|
var children = e.currentMenu.getElementsByTagName('*'),
|
|
len = children.length;
|
|
|
|
while( len-- )
|
|
children[ len ].parentNode.removeChild( children[ len ] );
|
|
|
|
e.currentMenu.removeEventListener( closeEvent, stopPropagation );
|
|
doc.body.removeChild( e.currentMenu );
|
|
e.currentMenu = null;
|
|
return false;
|
|
},
|
|
/** stop propagation func, so that we don't have to use anonymous funcs **/
|
|
stopPropagation = function(e){ e.stopPropagation(); },
|
|
openContext = function( e, x, y ) {
|
|
|
|
closeContext(null);
|
|
activeMenu.push( e );
|
|
|
|
//go through all the options and make the div
|
|
var div = doc.createElement('div'),
|
|
a,
|
|
marginOffset = 4,
|
|
opts = e.options,
|
|
leftOffset = x - marginOffset,
|
|
topOffset = y - marginOffset,
|
|
width = 0, height = 0;
|
|
|
|
div.className = "pk_contextMenu " + e.menuClass;
|
|
div.id = e.token;
|
|
|
|
for( var i = 0, len = opts.length; i < len; ++i ) {
|
|
if( opts[ i ].isHTML )
|
|
{
|
|
a = doc.createElement('div');
|
|
a.innerHTML = opts[ i ].isHTML;
|
|
div.appendChild( a );
|
|
}
|
|
else {
|
|
a = doc.createElement('a');
|
|
a.className = 'pk_ctx_action';
|
|
a.cnt = 1;
|
|
a.innerHTML = opts[ i ].name;
|
|
a.callback = opts[ i ].callback;
|
|
|
|
a.addEventListener( 'click', a.callback, false );
|
|
|
|
div.appendChild( a );
|
|
}
|
|
}
|
|
|
|
e.currentMenu = div;
|
|
div.addEventListener( closeEvent, stopPropagation, false );
|
|
|
|
doc.body.appendChild( div );
|
|
|
|
width = div.offsetWidth;
|
|
height = div.offsetHeight;
|
|
|
|
if( win.innerWidth < ( leftOffset + width ) && win.innerHeight < ( topOffset + height ) )
|
|
div.style.cssText = "top:" + ( topOffset - height ) + "px;left:" + ( leftOffset - width ) + "px;";
|
|
else if( win.innerWidth < ( leftOffset + width ) )
|
|
div.style.cssText = "top:" + ( topOffset ) + "px;left:" + ( leftOffset - width ) + "px;";
|
|
else if( win.innerHeight < ( topOffset + height ) )
|
|
div.style.cssText = "top:" + ( topOffset - height ) + "px;left:" + ( leftOffset ) + "px;";
|
|
else
|
|
div.style.cssText = "top:" + ( topOffset ) + "px;left:" + ( leftOffset ) + "px;";
|
|
|
|
if (e.onOpen) {
|
|
e.onOpen ( e, div );
|
|
}
|
|
|
|
return false;
|
|
},
|
|
openMenu = function( e )
|
|
{
|
|
if (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
}
|
|
else {
|
|
e = {pageX:0, pageY:0};
|
|
}
|
|
|
|
// ----
|
|
var instance = getInstance ( this );
|
|
var pageX = e.pageX || e.clientX + doc.documentElement.scrollLeft;
|
|
var pageY = e.pageY || e.clientY + doc.documentElement.scrollTop;
|
|
|
|
if (!instance) return false;
|
|
|
|
instance.curr_target = e.target || e.srcElement;
|
|
|
|
openContext ( instance, pageX, pageY );
|
|
},
|
|
getInstance = function( elem ) {
|
|
return contextStorage[ elem.getAttribute( 'data-token' ) ];
|
|
};
|
|
|
|
/**
|
|
* Context Menu Constructor
|
|
**/
|
|
var contextMenu = namespace.contextMenu = function( elem, options ) {
|
|
if (!(this instanceof contextMenu)) return new contextMenu( elem, options );
|
|
if (!options) options = {};
|
|
|
|
var open_events = ['contextmenu', 'longpress'];
|
|
|
|
this.elem = elem;
|
|
this.options = [];
|
|
this.menuClass = options.className || 'pk_open';
|
|
this.curr_target = null;
|
|
|
|
// modified context menu to open only when double click + no movement
|
|
// if (elem) elem.addEventListener( 'contextmenu', openMenu, false );
|
|
if (elem) elem.addEventListener( 'pk_ctxmn', openMenu, false );
|
|
|
|
this.token = ++_id;
|
|
if (elem) elem.setAttribute( 'data-token', this.token );
|
|
|
|
contextStorage[ this.token ] = this;
|
|
};
|
|
/**
|
|
* Wrapper to the private openMenu function
|
|
**/
|
|
contextMenu.prototype.open = function( e ) {
|
|
openMenu.call( this.elem, e );
|
|
};
|
|
contextMenu.prototype.close = function( e ) {
|
|
closeContext();
|
|
};
|
|
contextMenu.prototype.openWithToken = function( token, x, y ) {
|
|
openContext( contextStorage[ token ], x||0, y||0 );
|
|
};
|
|
/**
|
|
* Closes context and removes it fully
|
|
**/
|
|
contextMenu.prototype.destroy = function() {
|
|
// this.elem.removeEventListener( 'contextmenu', openMenu );
|
|
this.elem.removeEventListener( 'pk_ctxmn', openMenu );
|
|
|
|
closeContext();
|
|
contextStorage[ this.token ] = null;
|
|
|
|
return false;
|
|
},
|
|
/**
|
|
* Adds option
|
|
* @param string name of the option
|
|
* @param function to run when its chosen
|
|
* @param if this is set, then append the HTML instead of its name in its position
|
|
* @param initialization code to run when the object is appended to the dom
|
|
**/
|
|
contextMenu.prototype.addOption = function( name, callback, isHTML ) {
|
|
var q = this;
|
|
this.options.push({ "name" : name, "callback" : function( e ) {
|
|
|
|
callback && callback( q, q._open );
|
|
closeContext( q, true );
|
|
},
|
|
"isHTML" : isHTML
|
|
});
|
|
};
|
|
|
|
|
|
// todo touch controls too? ####
|
|
doc.addEventListener( closeEvent[0], closeContext, false );
|
|
doc.addEventListener( 'killCTX', closeContext, false );
|
|
|
|
|
|
PKAE._deps.ContextMenu = contextMenu;
|
|
|
|
})( window, document, PKAudioEditor ); |